aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/lang3/time/FormatCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/lang3/time/FormatCache.java')
-rw-r--r--src/main/java/org/apache/commons/lang3/time/FormatCache.java243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/lang3/time/FormatCache.java b/src/main/java/org/apache/commons/lang3/time/FormatCache.java
new file mode 100644
index 000000000..38dbe1677
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/time/FormatCache.java
@@ -0,0 +1,243 @@
+/*
+ * 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.Objects;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.LocaleUtils;
+
+/**
+ * FormatCache is a cache and factory for {@link Format}s.
+ *
+ * @param <F> The Format type.
+ *
+ * @since 3.0
+ */
+// TODO: Before making public move from getDateTimeInstance(Integer, ...) to int; or some other approach.
+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<ArrayKey, F> cInstanceCache = new ConcurrentHashMap<>(7);
+
+ private static final ConcurrentMap<ArrayKey, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7);
+
+ /**
+ * Gets a formatter instance using the default pattern in the
+ * default time zone and locale.
+ *
+ * @return a date/time formatter
+ */
+ public F getInstance() {
+ return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ /**
+ * Gets a formatter instance using the specified pattern, time zone
+ * and locale.
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible
+ * pattern, non-null
+ * @param timeZone the time zone, null means use the default TimeZone
+ * @param locale the locale, null means use the default Locale
+ * @return a pattern based date/time formatter
+ * @throws NullPointerException if pattern is {@code null}
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ public F getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
+ Objects.requireNonNull(pattern, "pattern");
+ final TimeZone actualTimeZone = TimeZones.toTimeZone(timeZone);
+ final Locale actualLocale = LocaleUtils.toLocale(locale);
+ final ArrayKey key = new ArrayKey(pattern, actualTimeZone, actualLocale);
+ return cInstanceCache.computeIfAbsent(key, k -> createInstance(pattern, actualTimeZone, actualLocale));
+ }
+
+ /**
+ * Create a format instance using the specified pattern, time zone
+ * and locale.
+ *
+ * @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}
+ */
+ protected abstract F createInstance(String pattern, TimeZone timeZone, Locale locale);
+
+ /**
+ * Gets a date/time formatter instance using the specified style,
+ * time zone and locale.
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date, null means use default Locale
+ * @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
+ */
+ // This must remain private, see LANG-884
+ private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
+ locale = LocaleUtils.toLocale(locale);
+ final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
+ return getInstance(pattern, timeZone, locale);
+ }
+
+ /**
+ * Gets a date/time formatter instance using the specified style,
+ * time zone and locale.
+ *
+ * @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, null means use default Locale
+ * @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
+ */
+ // package protected, for access from FastDateFormat; do not make public or protected
+ F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
+ return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
+ }
+
+ /**
+ * Gets a date formatter instance using the specified style,
+ * time zone and locale.
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date, null means use default Locale
+ * @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
+ */
+ // package protected, for access from FastDateFormat; do not make public or protected
+ F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
+ return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
+ }
+
+ /**
+ * Gets a time formatter instance using the specified style,
+ * time zone and locale.
+ *
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
+ * @param timeZone optional time zone, overrides time zone of
+ * formatted date, null means use default Locale
+ * @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
+ */
+ // package protected, for access from FastDateFormat; do not make public or protected
+ F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) {
+ return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
+ }
+
+ /**
+ * Gets a date/time format for the specified styles and locale.
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
+ * @param locale The non-null locale of the desired format
+ * @return a localized standard date/time format
+ * @throws IllegalArgumentException if the Locale has no date/time pattern defined
+ */
+ // package protected, for access from test code; do not make public or protected
+ static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
+ final Locale safeLocale = LocaleUtils.toLocale(locale);
+ final ArrayKey key = new ArrayKey(dateStyle, timeStyle, safeLocale);
+ return cDateTimeInstanceCache.computeIfAbsent(key, k -> {
+ try {
+ final DateFormat formatter;
+ if (dateStyle == null) {
+ formatter = DateFormat.getTimeInstance(timeStyle.intValue(), safeLocale);
+ } else if (timeStyle == null) {
+ formatter = DateFormat.getDateInstance(dateStyle.intValue(), safeLocale);
+ } else {
+ formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), safeLocale);
+ }
+ return ((SimpleDateFormat) formatter).toPattern();
+ } catch (final ClassCastException ex) {
+ throw new IllegalArgumentException("No date time pattern for locale: " + safeLocale);
+ }
+ });
+ }
+
+ /**
+ * Helper class to hold multipart Map keys as arrays.
+ */
+ private static final class ArrayKey {
+
+ private static int computeHashCode(final Object[] keys) {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(keys);
+ return result;
+ }
+
+ private final Object[] keys;
+ private final int hashCode;
+
+ /**
+ * Constructs an instance of {@link MultipartKey} to hold the specified objects.
+ *
+ * @param keys the set of objects that make up the key. Each key may be null.
+ */
+ ArrayKey(final Object... keys) {
+ this.keys = keys;
+ this.hashCode = computeHashCode(keys);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ArrayKey other = (ArrayKey) obj;
+ return Arrays.deepEquals(keys, other.keys);
+ }
+
+
+ }
+
+}