You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ba...@apache.org on 2011/04/20 08:51:16 UTC
svn commit: r1095299 - in /commons/proper/lang/trunk/src:
main/java/org/apache/commons/lang3/time/FastDateFormat.java
main/java/org/apache/commons/lang3/time/FormatCache.java
test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
Author: bayard
Date: Wed Apr 20 06:51:16 2011
New Revision: 1095299
URL: http://svn.apache.org/viewvc?rev=1095299&view=rev
Log:
Refactoring FastDateFormat per LANG-462 to use the FormatCache class created for an upcoming DateParser functionality. I've kept FormatCache package-private for now.
Added:
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java (with props)
Modified:
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java?rev=1095299&r1=1095298&r2=1095299&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java (original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java Wed Apr 20 06:51:16 2011
@@ -23,16 +23,14 @@ import java.text.DateFormatSymbols;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.Validate;
@@ -99,15 +97,15 @@ public class FastDateFormat extends Form
*/
public static final int SHORT = DateFormat.SHORT;
- //@GuardedBy("this")
- private static String cDefaultPattern; // lazily initialised by getInstance()
+ private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() {
+ @Override
+ protected FastDateFormat createInstance(String pattern, TimeZone timeZone, Locale locale) {
+ return new FastDateFormat(pattern, timeZone, locale);
+ }
+ };
- private static final Map<FastDateFormat, FastDateFormat> cInstanceCache =
- new HashMap<FastDateFormat, FastDateFormat>(7);
- private static final Map<Object, FastDateFormat> cDateInstanceCache = new HashMap<Object, FastDateFormat>(7);
- private static final Map<Object, FastDateFormat> cTimeInstanceCache = new HashMap<Object, FastDateFormat>(7);
- private static final Map<Object, FastDateFormat> cDateTimeInstanceCache = new HashMap<Object, FastDateFormat>(7);
- private static final Map<Object, String> cTimeZoneDisplayCache = new HashMap<Object, String>(7);
+ private static ConcurrentMap<TimeZoneDisplayKey, String> cTimeZoneDisplayCache =
+ new ConcurrentHashMap<TimeZoneDisplayKey, String>(7);
/**
* The pattern.
@@ -118,18 +116,10 @@ public class FastDateFormat extends Form
*/
private final TimeZone mTimeZone;
/**
- * Whether the time zone overrides any on Calendars.
- */
- private final boolean mTimeZoneForced;
- /**
* The locale.
*/
private final Locale mLocale;
/**
- * Whether the locale overrides the default.
- */
- private final boolean mLocaleForced;
- /**
* The parsed rules.
*/
private transient Rule[] mRules;
@@ -146,7 +136,7 @@ public class FastDateFormat extends Form
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
- return getInstance(getDefaultPattern(), null, null);
+ return cache.getDateTimeInstance(SHORT, SHORT, null, null);
}
/**
@@ -159,7 +149,7 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(String pattern) {
- return getInstance(pattern, null, null);
+ return cache.getInstance(pattern, null, null);
}
/**
@@ -174,7 +164,7 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(String pattern, TimeZone timeZone) {
- return getInstance(pattern, timeZone, null);
+ return cache.getInstance(pattern, timeZone, null);
}
/**
@@ -188,7 +178,7 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(String pattern, Locale locale) {
- return getInstance(pattern, null, locale);
+ return cache.getInstance(pattern, null, locale);
}
/**
@@ -204,15 +194,8 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
- public static synchronized FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
- FastDateFormat emptyFormat = new FastDateFormat(pattern, timeZone, locale);
- FastDateFormat format = cInstanceCache.get(emptyFormat);
- if (format == null) {
- format = emptyFormat;
- format.init(); // convert shell format into usable one
- cInstanceCache.put(format, format); // this is OK!
- }
- return format;
+ public static FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
+ return cache.getInstance(pattern, timeZone, locale);
}
//-----------------------------------------------------------------------
@@ -227,7 +210,7 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getDateInstance(int style) {
- return getDateInstance(style, null, null);
+ return cache.getDateTimeInstance(style, null, null, null);
}
/**
@@ -242,7 +225,7 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getDateInstance(int style, Locale locale) {
- return getDateInstance(style, null, locale);
+ return cache.getDateTimeInstance(style, null, null, locale);
}
/**
@@ -258,8 +241,9 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getDateInstance(int style, TimeZone timeZone) {
- return getDateInstance(style, timeZone, null);
+ return cache.getDateTimeInstance(style, null, timeZone, null);
}
+
/**
* <p>Gets a date formatter instance using the specified style, time
* zone and locale.</p>
@@ -272,31 +256,8 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
*/
- public static synchronized FastDateFormat getDateInstance(int style, TimeZone timeZone, Locale locale) {
- Object key = Integer.valueOf(style);
- if (timeZone != null) {
- key = new Pair(key, timeZone);
- }
-
- if (locale == null) {
- locale = Locale.getDefault();
- }
-
- key = new Pair(key, locale);
-
- FastDateFormat format = cDateInstanceCache.get(key);
- if (format == null) {
- try {
- SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance(style, locale);
- String pattern = formatter.toPattern();
- format = getInstance(pattern, timeZone, locale);
- cDateInstanceCache.put(key, format);
-
- } catch (ClassCastException ex) {
- throw new IllegalArgumentException("No date pattern for locale: " + locale);
- }
- }
- return format;
+ public static FastDateFormat getDateInstance(int style, TimeZone timeZone, Locale locale) {
+ return cache.getDateTimeInstance(style, null, timeZone, locale);
}
//-----------------------------------------------------------------------
@@ -311,7 +272,7 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getTimeInstance(int style) {
- return getTimeInstance(style, null, null);
+ return cache.getDateTimeInstance(null, style, null, null);
}
/**
@@ -326,7 +287,7 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getTimeInstance(int style, Locale locale) {
- return getTimeInstance(style, null, locale);
+ return cache.getDateTimeInstance(null, style, null, locale);
}
/**
@@ -342,7 +303,7 @@ public class FastDateFormat extends Form
* @since 2.1
*/
public static FastDateFormat getTimeInstance(int style, TimeZone timeZone) {
- return getTimeInstance(style, timeZone, null);
+ return cache.getDateTimeInstance(null, style, timeZone, null);
}
/**
@@ -357,32 +318,8 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
*/
- public static synchronized FastDateFormat getTimeInstance(int style, TimeZone timeZone, Locale locale) {
- Object key = Integer.valueOf(style);
- if (timeZone != null) {
- key = new Pair(key, timeZone);
- }
- if (locale != null) {
- key = new Pair(key, locale);
- }
-
- FastDateFormat format = cTimeInstanceCache.get(key);
- if (format == null) {
- if (locale == null) {
- locale = Locale.getDefault();
- }
-
- try {
- SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getTimeInstance(style, locale);
- String pattern = formatter.toPattern();
- format = getInstance(pattern, timeZone, locale);
- cTimeInstanceCache.put(key, format);
-
- } catch (ClassCastException ex) {
- throw new IllegalArgumentException("No date pattern for locale: " + locale);
- }
- }
- return format;
+ public static FastDateFormat getTimeInstance(int style, TimeZone timeZone, Locale locale) {
+ return cache.getDateTimeInstance(null, style, timeZone, locale);
}
//-----------------------------------------------------------------------
@@ -397,9 +334,8 @@ public class FastDateFormat extends Form
* pattern defined
* @since 2.1
*/
- public static FastDateFormat getDateTimeInstance(
- int dateStyle, int timeStyle) {
- return getDateTimeInstance(dateStyle, timeStyle, null, null);
+ public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
+ return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
}
/**
@@ -414,9 +350,8 @@ public class FastDateFormat extends Form
* pattern defined
* @since 2.1
*/
- public static FastDateFormat getDateTimeInstance(
- int dateStyle, int timeStyle, Locale locale) {
- return getDateTimeInstance(dateStyle, timeStyle, null, locale);
+ public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
+ return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
}
/**
@@ -432,8 +367,7 @@ public class FastDateFormat extends Form
* pattern defined
* @since 2.1
*/
- public static FastDateFormat getDateTimeInstance(
- int dateStyle, int timeStyle, TimeZone timeZone) {
+ public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
@@ -449,32 +383,9 @@ public class FastDateFormat extends Form
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
- public static synchronized FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone,
- Locale locale) {
-
- Object key = new Pair(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle));
- if (timeZone != null) {
- key = new Pair(key, timeZone);
- }
- if (locale == null) {
- locale = Locale.getDefault();
- }
- key = new Pair(key, locale);
-
- FastDateFormat format = cDateTimeInstanceCache.get(key);
- if (format == null) {
- try {
- SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateTimeInstance(dateStyle, timeStyle,
- locale);
- String pattern = formatter.toPattern();
- format = getInstance(pattern, timeZone, locale);
- cDateTimeInstanceCache.put(key, format);
-
- } catch (ClassCastException ex) {
- throw new IllegalArgumentException("No date time pattern for locale: " + locale);
- }
- }
- return format;
+ public static FastDateFormat getDateTimeInstance(
+ int dateStyle, int timeStyle, TimeZone timeZone, Locale locale) {
+ return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
//-----------------------------------------------------------------------
@@ -488,68 +399,42 @@ public class FastDateFormat extends Form
* @param locale the locale to use
* @return the textual name of the time zone
*/
- static synchronized String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
- Object key = new TimeZoneDisplayKey(tz, daylight, style, locale);
+ static String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
+ TimeZoneDisplayKey key = new TimeZoneDisplayKey(tz, daylight, style, locale);
String value = cTimeZoneDisplayCache.get(key);
if (value == null) {
// This is a very slow call, so cache the results.
value = tz.getDisplayName(daylight, style, locale);
- cTimeZoneDisplayCache.put(key, value);
+ String prior = cTimeZoneDisplayCache.putIfAbsent(key, value);
+ if (prior != null) {
+ value= prior;
+ }
}
return value;
}
- /**
- * <p>Gets the default pattern.</p>
- *
- * @return the default pattern
- */
- private static synchronized String getDefaultPattern() {
- if (cDefaultPattern == null) {
- cDefaultPattern = new SimpleDateFormat().toPattern();
- }
- return cDefaultPattern;
- }
-
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
- * @param pattern {@link java.text.SimpleDateFormat} compatible
- * pattern
- * @param timeZone time zone to use, <code>null</code> means use
- * default for <code>Date</code> and value within for
- * <code>Calendar</code>
- * @param locale locale, <code>null</code> means use system
- * default
- * @throws IllegalArgumentException if pattern is invalid or
- * <code>null</code>
+ * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
+ * @param timeZone non-null time zone to use
+ * @param locale non-null locale to use
+ * @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(String pattern, TimeZone timeZone, Locale locale) {
- super();
- if (pattern == null) {
- throw new IllegalArgumentException("The pattern must not be null");
- }
mPattern = pattern;
-
- mTimeZoneForced = (timeZone != null);
- if (timeZone == null) {
- timeZone = TimeZone.getDefault();
- }
mTimeZone = timeZone;
-
- mLocaleForced = (locale != null);
- if (locale == null) {
- locale = Locale.getDefault();
- }
mLocale = locale;
+
+ init();
}
/**
* <p>Initializes the instance for first use.</p>
*/
- protected void init() {
+ private void init() {
List<Rule> rulesList = parsePattern();
mRules = rulesList.toArray(new Rule[rulesList.size()]);
@@ -662,9 +547,9 @@ public class FastDateFormat extends Form
break;
case 'z': // time zone (text)
if (tokenLen >= 4) {
- rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.LONG);
+ rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.LONG);
} else {
- rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.SHORT);
+ rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.SHORT);
}
break;
case 'Z': // time zone (value)
@@ -812,7 +697,7 @@ public class FastDateFormat extends Form
* @return the formatted string
*/
public String format(Date date) {
- Calendar c = new GregorianCalendar(mTimeZone, mLocale);
+ Calendar c = Calendar.getInstance(mTimeZone, mLocale);
c.setTime(date);
return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
}
@@ -849,7 +734,7 @@ public class FastDateFormat extends Form
* @return the specified string buffer
*/
public StringBuffer format(Date date, StringBuffer buf) {
- Calendar c = new GregorianCalendar(mTimeZone);
+ Calendar c = Calendar.getInstance(mTimeZone, mLocale);
c.setTime(date);
return applyRules(c, buf);
}
@@ -863,11 +748,6 @@ public class FastDateFormat extends Form
* @return the specified string buffer
*/
public StringBuffer format(Calendar calendar, StringBuffer buf) {
- if (mTimeZoneForced) {
- calendar.getTimeInMillis(); /// LANG-538
- calendar = (Calendar) calendar.clone();
- calendar.setTimeZone(mTimeZone);
- }
return applyRules(calendar, buf);
}
@@ -880,10 +760,8 @@ public class FastDateFormat extends Form
* @return the specified string buffer
*/
protected StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
- Rule[] rules = mRules;
- int len = mRules.length;
- for (int i = 0; i < len; i++) {
- rules[i].appendTo(buf, calendar);
+ for (Rule rule : mRules) {
+ rule.appendTo(buf, calendar);
}
return buf;
}
@@ -918,10 +796,7 @@ public class FastDateFormat extends Form
/**
* <p>Gets the time zone used by this formatter.</p>
*
- * <p>This zone is always used for <code>Date</code> formatting.
- * If a <code>Calendar</code> is passed in to be formatted, the
- * time zone on that may be used depending on
- * {@link #getTimeZoneOverridesCalendar()}.</p>
+ * <p>This zone is always used for <code>Date</code> formatting. </p>
*
* @return the time zone
*/
@@ -930,17 +805,6 @@ public class FastDateFormat extends Form
}
/**
- * <p>Returns <code>true</code> if the time zone of the
- * calendar overrides the time zone of the formatter.</p>
- *
- * @return <code>true</code> if time zone of formatter
- * overridden for calendars
- */
- public boolean getTimeZoneOverridesCalendar() {
- return mTimeZoneForced;
- }
-
- /**
* <p>Gets the locale used by this formatter.</p>
*
* @return the locale
@@ -976,16 +840,9 @@ public class FastDateFormat extends Form
return false;
}
FastDateFormat other = (FastDateFormat) obj;
- if (
- (mPattern == other.mPattern || mPattern.equals(other.mPattern)) &&
- (mTimeZone == other.mTimeZone || mTimeZone.equals(other.mTimeZone)) &&
- (mLocale == other.mLocale || mLocale.equals(other.mLocale)) &&
- (mTimeZoneForced == other.mTimeZoneForced) &&
- (mLocaleForced == other.mLocaleForced)
- ) {
- return true;
- }
- return false;
+ return mPattern.equals(other.mPattern)
+ && mTimeZone.equals(other.mTimeZone)
+ && mLocale.equals(other.mLocale);
}
/**
@@ -995,13 +852,7 @@ public class FastDateFormat extends Form
*/
@Override
public int hashCode() {
- int total = 0;
- total += mPattern.hashCode();
- total += mTimeZone.hashCode();
- total += (mTimeZoneForced ? 1 : 0);
- total += mLocale.hashCode();
- total += (mLocaleForced ? 1 : 0);
- return total;
+ return mPattern.hashCode() + 13 * (mTimeZone.hashCode() + 13 * mLocale.hashCode());
}
/**
@@ -1517,9 +1368,6 @@ public class FastDateFormat extends Form
*/
private static class TimeZoneNameRule implements Rule {
private final TimeZone mTimeZone;
- private final boolean mTimeZoneForced;
- private final Locale mLocale;
- private final int mStyle;
private final String mStandard;
private final String mDaylight;
@@ -1527,55 +1375,31 @@ public class FastDateFormat extends Form
* Constructs an instance of <code>TimeZoneNameRule</code> with the specified properties.
*
* @param timeZone the time zone
- * @param timeZoneForced if <code>true</code> the time zone is forced into standard and daylight
* @param locale the locale
* @param style the style
*/
- TimeZoneNameRule(TimeZone timeZone, boolean timeZoneForced, Locale locale, int style) {
+ TimeZoneNameRule(TimeZone timeZone, Locale locale, int style) {
mTimeZone = timeZone;
- mTimeZoneForced = timeZoneForced;
- mLocale = locale;
- mStyle = style;
- if (timeZoneForced) {
- mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
- mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
- } else {
- mStandard = null;
- mDaylight = null;
- }
+ mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
+ mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
}
/**
* {@inheritDoc}
*/
public int estimateLength() {
- if (mTimeZoneForced) {
- return Math.max(mStandard.length(), mDaylight.length());
- } else if (mStyle == TimeZone.SHORT) {
- return 4;
- } else {
- return 40;
- }
+ return Math.max(mStandard.length(), mDaylight.length());
}
/**
* {@inheritDoc}
*/
public void appendTo(StringBuffer buffer, Calendar calendar) {
- if (mTimeZoneForced) {
- if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
- buffer.append(mDaylight);
- } else {
- buffer.append(mStandard);
- }
+ if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
+ buffer.append(mDaylight);
} else {
- TimeZone timeZone = calendar.getTimeZone();
- if (timeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
- buffer.append(getTimeZoneDisplay(timeZone, true, mStyle, mLocale));
- } else {
- buffer.append(getTimeZoneDisplay(timeZone, false, mStyle, mLocale));
- }
+ buffer.append(mStandard);
}
}
}
@@ -1665,7 +1489,7 @@ public class FastDateFormat extends Form
*/
@Override
public int hashCode() {
- return mStyle * 31 + mLocale.hashCode();
+ return (mStyle * 31 + mLocale.hashCode() ) * 31 + mTimeZone.hashCode();
}
/**
@@ -1686,67 +1510,4 @@ public class FastDateFormat extends Form
return false;
}
}
-
- // ----------------------------------------------------------------------
- /**
- * <p>Helper class for creating compound objects.</p>
- *
- * <p>One use for this class is to create a hashtable key
- * out of multiple objects.</p>
- */
- private static class Pair {
- private final Object mObj1;
- private final Object mObj2;
-
- /**
- * Constructs an instance of <code>Pair</code> to hold the specified objects.
- * @param obj1 one object in the pair
- * @param obj2 second object in the pair
- */
- public Pair(Object obj1, Object obj2) {
- mObj1 = obj1;
- mObj2 = obj2;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (!(obj instanceof Pair)) {
- return false;
- }
-
- Pair key = (Pair)obj;
-
- return
- (mObj1 == null ?
- key.mObj1 == null : mObj1.equals(key.mObj1)) &&
- (mObj2 == null ?
- key.mObj2 == null : mObj2.equals(key.mObj2));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return
- (mObj1 == null ? 0 : mObj1.hashCode()) +
- (mObj2 == null ? 0 : mObj2.hashCode());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- return "[" + mObj1 + ':' + mObj2 + ']';
- }
- }
-
}
Added: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java?rev=1095299&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java (added)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java Wed Apr 20 06:51:16 2011
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * <p>FormatCache is a cache and factory for {@link Format}s.</p>
+ *
+ * @since 3.0
+ * @version $Id: FormatCache 892161 2009-12-18 07:21:10Z $
+ */
+abstract class FormatCache<F extends Format> {
+ /**
+ * No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
+ */
+ static final int NONE= -1;
+
+ private final ConcurrentMap<MultipartKey, F> cInstanceCache
+ = new ConcurrentHashMap<MultipartKey, F>(7);
+
+ private final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
+ = new ConcurrentHashMap<MultipartKey, String>(7);
+
+ /**
+ * <p>Gets a formatter instance using the default pattern in the
+ * default timezone and locale.</p>
+ *
+ * @return a date/time formatter
+ */
+ public F getInstance() {
+ return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ /**
+ * <p>Gets a formatter instance using the specified pattern, time zone
+ * and locale.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern
+ * @param timeZone the non-null time zone
+ * @param locale the non-null locale
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ * or <code>null</code>
+ */
+ public F getInstance(String pattern, TimeZone timeZone, Locale locale) {
+ if (pattern == null) {
+ throw new NullPointerException("pattern must not be null");
+ }
+ if (timeZone == null) {
+ timeZone = TimeZone.getDefault();
+ }
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ MultipartKey key = new MultipartKey(pattern, timeZone, locale);
+ F format = cInstanceCache.get(key);
+ if (format == null) {
+ format = createInstance(pattern, timeZone, locale);
+ F previousValue= cInstanceCache.putIfAbsent(key, format);
+ if (previousValue != null) {
+ // another thread snuck in and did the same work
+ // we should return the instance that is in ConcurrentMap
+ format= previousValue;
+ }
+ }
+ return format;
+ }
+
+ /**
+ * <p>Create a format instance using the specified pattern, time zone
+ * and locale.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
+ * @param timeZone time zone, this will not be null.
+ * @param locale locale, this will not be null.
+ * @return a pattern based date/time formatter
+ * @throws IllegalArgumentException if pattern is invalid
+ * or <code>null</code>
+ */
+ abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
+
+ /**
+ * <p>Gets a date/time formatter instance using the specified style,
+ * time zone and locale.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date
+ * @param locale optional locale, overrides system locale
+ * @return a localized standard date/time formatter
+ * @throws IllegalArgumentException if the Locale has no date/time
+ * pattern defined
+ */
+ public F getDateTimeInstance(Integer dateStyle, Integer timeStyle, TimeZone timeZone, Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
+
+ String pattern = cDateTimeInstanceCache.get(key);
+ if (pattern == null) {
+ try {
+ DateFormat formatter;
+ if (dateStyle == null) {
+ formatter = DateFormat.getTimeInstance(timeStyle, locale);
+ }
+ else if (timeStyle == null) {
+ formatter = DateFormat.getDateInstance(dateStyle, locale);
+ }
+ else {
+ formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
+ }
+ pattern = ((SimpleDateFormat)formatter).toPattern();
+ String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
+ if (previous != null) {
+ // even though it doesn't matter if another thread put the pattern
+ // it's still good practice to return the String instance that is
+ // actually in the ConcurrentMap
+ pattern= previous;
+ }
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("No date time pattern for locale: " + locale);
+ }
+ }
+
+ return getInstance(pattern, timeZone, locale);
+ }
+
+ // ----------------------------------------------------------------------
+ /**
+ * <p>Helper class to hold multi-part Map keys</p>
+ */
+ private static class MultipartKey {
+ private final Object[] keys;
+ private int hashCode;
+
+ /**
+ * Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
+ * @param keys the set of objects that make up the key. Each key may be null.
+ */
+ public MultipartKey(Object... keys) {
+ this.keys = keys;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ( obj instanceof MultipartKey == false ) {
+ return false;
+ }
+ return Arrays.equals(keys, ((MultipartKey)obj).keys);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ if(hashCode==0) {
+ int rc= 0;
+ for(Object key : keys) {
+ if(key!=null) {
+ rc= rc*7 + key.hashCode();
+ }
+ }
+ hashCode= rc;
+ }
+ return hashCode;
+ }
+ }
+
+}
Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java?rev=1095299&r1=1095298&r2=1095299&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java (original)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java Wed Apr 20 06:51:16 2011
@@ -56,8 +56,6 @@ public class FastDateFormatTest extends
assertEquals("MM/DD/yyyy", format1.getPattern());
assertEquals(TimeZone.getDefault(), format1.getTimeZone());
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
- assertEquals(false, format1.getTimeZoneOverridesCalendar());
- assertEquals(false, format2.getTimeZoneOverridesCalendar());
}
public void test_getInstance_String_TimeZone() {
@@ -77,9 +75,7 @@ public class FastDateFormatTest extends
assertTrue(format1 != format2); // -- junit 3.8 version -- assertFalse(format1 == format2);
assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone());
- assertEquals(true, format1.getTimeZoneOverridesCalendar());
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
- assertEquals(false, format2.getTimeZoneOverridesCalendar());
assertSame(format3, format4);
assertTrue(format3 != format5); // -- junit 3.8 version -- assertFalse(format3 == format5);
assertTrue(format4 != format6); // -- junit 3.8 version -- assertFalse(format3 == format5);
@@ -164,9 +160,6 @@ public class FastDateFormatTest extends
assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone());
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
assertEquals(TimeZone.getDefault(), format3.getTimeZone());
- assertEquals(true, format1.getTimeZoneOverridesCalendar());
- assertEquals(false, format2.getTimeZoneOverridesCalendar());
- assertEquals(true, format3.getTimeZoneOverridesCalendar());
assertEquals(Locale.GERMANY, format1.getLocale());
assertEquals(Locale.GERMANY, format2.getLocale());
assertEquals(Locale.GERMANY, format3.getLocale());
@@ -183,8 +176,6 @@ public class FastDateFormatTest extends
try {
Locale.setDefault(Locale.US);
TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
- FastDateFormat fdf = null;
- SimpleDateFormat sdf = null;
GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 00, 00);
@@ -193,8 +184,8 @@ public class FastDateFormatTest extends
long millis1 = date1.getTime();
long millis2 = date2.getTime();
- fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
- sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
assertEquals(sdf.format(date1), fdf.format(date1));
assertEquals("2003-01-10T15:33:20", fdf.format(date1));
assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
@@ -208,7 +199,6 @@ public class FastDateFormatTest extends
assertEquals("-0500", fdf.format(cal1));
assertEquals("-0500", fdf.format(millis1));
- fdf = FastDateFormat.getInstance("Z");
assertEquals("-0400", fdf.format(date2));
assertEquals("-0400", fdf.format(cal2));
assertEquals("-0400", fdf.format(millis2));
@@ -218,7 +208,6 @@ public class FastDateFormatTest extends
assertEquals("-05:00", fdf.format(cal1));
assertEquals("-05:00", fdf.format(millis1));
- fdf = FastDateFormat.getInstance("ZZ");
assertEquals("-04:00", fdf.format(date2));
assertEquals("-04:00", fdf.format(cal2));
assertEquals("-04:00", fdf.format(millis2));
@@ -228,14 +217,13 @@ public class FastDateFormatTest extends
fdf = FastDateFormat.getInstance(pattern);
sdf = new SimpleDateFormat(pattern);
assertEquals(sdf.format(date1), fdf.format(date1));
- assertEquals(sdf.format(date2), fdf.format(date2));
-
+ assertEquals(sdf.format(date2), fdf.format(date2));
} finally {
Locale.setDefault(realDefaultLocale);
TimeZone.setDefault(realDefaultZone);
}
}
-
+
/**
* Test case for {@link FastDateFormat#getDateInstance(int, java.util.Locale)}.
*/
@@ -283,7 +271,6 @@ public class FastDateFormatTest extends
* testLowYearPadding showed that the date was buggy
* This test confirms it, getting 366 back as a date
*/
- // TODO: Fix this problem
public void testSimpleDate() {
Calendar cal = Calendar.getInstance();
FastDateFormat format = FastDateFormat.getInstance("yyyy/MM/dd");
@@ -308,8 +295,6 @@ public class FastDateFormatTest extends
}
public void testLang538() {
- final String dateTime = "2009-10-16T16:42:16.000Z";
-
// more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
// for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT-8"));
@@ -317,7 +302,8 @@ public class FastDateFormatTest extends
cal.set(2009, 9, 16, 8, 42, 16);
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
- assertEquals("dateTime", dateTime, format.format(cal));
+ assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal.getTime()));
+ assertEquals("dateTime", "2009-10-16T08:42:16.000Z", format.format(cal));
}
public void testLang645() {