net.time4j.UnitPatterns Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2015 Meno Hochschild,
* -----------------------------------------------------------------------
* This file (UnitPatterns.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see .
* -----------------------------------------------------------------------
*/
package net.time4j;
import net.time4j.base.ResourceLoader;
import net.time4j.format.PluralCategory;
import net.time4j.format.RelativeTimeProvider;
import net.time4j.format.TextWidth;
import net.time4j.format.UnitPatternProvider;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Offers localized time unit patterns for formatting of durations.
*
* @author Meno Hochschild
* @since 3.0
* @see UnitPatternProvider
* @doctags.concurrency {immutable}
*/
final class UnitPatterns {
//~ Statische Felder/Initialisierungen --------------------------------
private static final int MIN_LIST_INDEX = 2;
private static final int MAX_LIST_INDEX = 7;
private static final ConcurrentMap CACHE = new ConcurrentHashMap<>();
private static final IsoUnit[] UNIT_IDS = {
CalendarUnit.YEARS,
CalendarUnit.MONTHS,
CalendarUnit.WEEKS,
CalendarUnit.DAYS,
ClockUnit.HOURS,
ClockUnit.MINUTES,
ClockUnit.SECONDS,
ClockUnit.MILLIS,
ClockUnit.MICROS,
ClockUnit.NANOS
};
private static final UnitPatternProvider PROVIDER;
private static final UnitPatternProvider FALLBACK;
static {
FALLBACK = new FallbackProvider();
UnitPatternProvider p = null;
for (UnitPatternProvider tmp : ResourceLoader.getInstance().services(UnitPatternProvider.class)) {
p = tmp;
break;
}
if (p == null) {
p = FALLBACK;
}
PROVIDER = p;
}
//~ Instanzvariablen --------------------------------------------------
private final Locale locale;
private final Map>> patterns;
private final Map> past;
private final Map> future;
private final Map> shortPast;
private final Map> shortFuture;
private final Map> list;
private final String now;
private final String yesterday;
private final String today;
private final String tomorrow;
//~ Konstruktoren -----------------------------------------------------
private UnitPatterns(Locale language) {
super();
this.locale = language;
Map>> map = new HashMap<>(10);
Map> mapPast = new HashMap<>(10);
Map> mapFuture = new HashMap<>(10);
Map> mapList = new HashMap<>(10);
Map> mapShortPast = new HashMap<>(10);
Map> mapShortFuture = new HashMap<>(10);
for (IsoUnit unit : UNIT_IDS) {
// Standard-Muster
Map> tmp1 = new EnumMap<>(TextWidth.class);
for (TextWidth width : TextWidth.values()) {
Map tmp2 = new EnumMap<>(PluralCategory.class);
for (PluralCategory cat : PluralCategory.values()) {
tmp2.put(cat, lookup(language, unit, width, cat));
}
tmp1.put(width, Collections.unmodifiableMap(tmp2));
}
map.put(
unit,
Collections.unmodifiableMap(tmp1));
if (!Character.isDigit(unit.getSymbol())) { // no subseconds
// Vergangenheit
Map tmp3 = new EnumMap<>(PluralCategory.class);
for (PluralCategory cat : PluralCategory.values()) {
tmp3.put(cat, lookup(language, unit, false, false, cat));
}
mapPast.put(
unit,
Collections.unmodifiableMap(tmp3));
Map tmp3a = new EnumMap<>(PluralCategory.class);
for (PluralCategory cat : PluralCategory.values()) {
tmp3a.put(cat, lookup(language, unit, false, true, cat));
}
mapShortPast.put(
unit,
Collections.unmodifiableMap(tmp3a));
// Zukunft
Map tmp4 = new EnumMap<>(PluralCategory.class);
for (PluralCategory cat : PluralCategory.values()) {
tmp4.put(cat, lookup(language, unit, true, false, cat));
}
mapFuture.put(
unit,
Collections.unmodifiableMap(tmp4));
Map tmp4a = new EnumMap<>(PluralCategory.class);
for (PluralCategory cat : PluralCategory.values()) {
tmp4a.put(cat, lookup(language, unit, true, true, cat));
}
mapShortFuture.put(
unit,
Collections.unmodifiableMap(tmp4a));
}
}
// Liste
for (int i = MIN_LIST_INDEX; i <= MAX_LIST_INDEX; i++) {
Integer index = Integer.valueOf(i);
Map tmp5 = new EnumMap<>(TextWidth.class);
for (TextWidth width : TextWidth.values()) {
tmp5.put(width, lookup(language, width, index));
}
mapList.put(
index,
Collections.unmodifiableMap(tmp5));
}
this.patterns = Collections.unmodifiableMap(map);
this.past = Collections.unmodifiableMap(mapPast);
this.future = Collections.unmodifiableMap(mapFuture);
this.shortPast = Collections.unmodifiableMap(mapShortPast);
this.shortFuture = Collections.unmodifiableMap(mapShortFuture);
this.list = Collections.unmodifiableMap(mapList);
String n;
String y = "";
String t1 = "";
String t2 = "";
try {
n = PROVIDER.getNowWord(language);
if (PROVIDER instanceof RelativeTimeProvider) {
RelativeTimeProvider rp = RelativeTimeProvider.class.cast(PROVIDER);
y = rp.getYesterdayWord(language);
t1 = rp.getTodayWord(language);
t2 = rp.getTomorrowWord(language);
}
} catch (MissingResourceException mre) {
n = FALLBACK.getNowWord(language); // should not happen
}
this.now = n;
this.yesterday = y;
this.today = t1;
this.tomorrow = t2;
}
//~ Methoden ----------------------------------------------------------
/**
* Factory method as constructor replacement.
*
* @param lang language setting
* @return chached instance
*/
static UnitPatterns of(Locale lang) {
if (lang == null) {
throw new NullPointerException("Missing language.");
}
UnitPatterns p = CACHE.get(lang);
if (p == null) {
p = new UnitPatterns(lang);
UnitPatterns old = CACHE.putIfAbsent(lang, p);
if (old != null) {
p = old;
}
}
return p;
}
/**
* Yields a unit pattern which optionally contains a placeholder
* of the form "{0}" standing for the count of units.
*
* @param width text width (ABBREVIATED as synonym for SHORT)
* @param category plural category
* @param unit associated iso unit
* @return unit pattern
*/
String getPattern(
TextWidth width,
PluralCategory category,
IsoUnit unit
) {
checkNull(width, category);
return this.patterns.get(unit).get(width).get(category);
}
/**
* Yields a unit pattern which optionally contains a placeholder
* of the form "{0}" standing for the count of units in the
* past.
*
* @param category plural category
* @param abbreviated using short form or not
* @param unit associated iso unit
* @return unit pattern in the past
* @since 3.6/4.4
*/
String getPatternInPast(
PluralCategory category,
boolean abbreviated,
IsoUnit unit
) {
checkNull(category);
if (abbreviated) {
return this.shortPast.get(unit).get(category);
} else {
return this.past.get(unit).get(category);
}
}
/**
* Yields a unit pattern which optionally contains a placeholder
* of the form "{0}" standing for the count of units in the
* future.
*
* @param category plural category
* @param abbreviated using short form or not
* @param unit associated iso unit
* @return unit pattern in the future
* @since 3.6/4.4
*/
String getPatternInFuture(
PluralCategory category,
boolean abbreviated,
IsoUnit unit
) {
checkNull(category);
if (abbreviated) {
return this.shortFuture.get(unit).get(category);
} else {
return this.future.get(unit).get(category);
}
}
/**
* Yields the localized word for the current time ("now").
*
* @return String
*/
String getNowWord() {
return this.now;
}
/**
* Yields the localized word for "yesterday".
*
* @return String (maybe empty)
* @since 3.6/4.4
*/
String getYesterdayWord() {
return this.yesterday;
}
/**
* Yields the localized word for "today".
*
* @return String (maybe empty)
* @since 3.6/4.4
*/
String getTodayWord() {
return this.today;
}
/**
* Yields the localized word for "tomorrow".
*
* @return String (maybe empty)
* @since 3.6/4.4
*/
String getTomorrowWord() {
return this.tomorrow;
}
/**
* Constructs a localized list pattern suitable for the use in
* {@link java.text.MessageFormat#format(String, Object[])}.
*
* @param width text width (ABBREVIATED as synonym for SHORT)
* @param size count of list items
* @return message format pattern with placeholders {0}, {1}, ..., {x}, ...
* @throws IllegalArgumentException if size is smaller than 2
*/
String getListPattern(
TextWidth width,
int size
) {
if (width == null) {
throw new NullPointerException("Missing width.");
}
if (
(size >= MIN_LIST_INDEX)
&& (size <= MAX_LIST_INDEX)
) {
return this.list.get(Integer.valueOf(size)).get(width);
}
return lookup(this.locale, width, size);
}
private static void checkNull(PluralCategory category) {
if (category == null) {
throw new NullPointerException("Missing plural category.");
}
}
private static void checkNull(
TextWidth width,
PluralCategory category
) {
if (width == null) {
throw new NullPointerException("Missing text width.");
}
checkNull(category);
}
private static char getID(IsoUnit unit) {
char unitID = unit.getSymbol();
if (unit == ClockUnit.MINUTES) {
return 'N';
}
return unitID;
}
private static String lookup(
Locale language,
IsoUnit unit,
TextWidth width,
PluralCategory category
) {
try {
return lookup(PROVIDER, language, getID(unit), width, category);
} catch (MissingResourceException mre) { // should not happen
return lookup(FALLBACK, language, getID(unit), width, category);
}
}
private static String lookup(
UnitPatternProvider p,
Locale language,
char unitID,
TextWidth width,
PluralCategory category
) {
switch (unitID) {
case 'Y':
return p.getYearPattern(language, width, category);
case 'M':
return p.getMonthPattern(language, width, category);
case 'W':
return p.getWeekPattern(language, width, category);
case 'D':
return p.getDayPattern(language, width, category);
case 'H':
return p.getHourPattern(language, width, category);
case 'N':
return p.getMinutePattern(language, width, category);
case 'S':
return p.getSecondPattern(language, width, category);
case '3':
return p.getMilliPattern(language, width, category);
case '6':
return p.getMicroPattern(language, width, category);
case '9':
return p.getNanoPattern(language, width, category);
default:
throw new UnsupportedOperationException("Unit-ID: " + unitID);
}
}
private static String lookup(
Locale language,
IsoUnit unit,
boolean future,
boolean abbreviated,
PluralCategory category
) {
try {
return lookup(PROVIDER, language, getID(unit), future, abbreviated, category);
} catch (MissingResourceException mre) { // should not happen
return lookup(FALLBACK, language, getID(unit), future, abbreviated, category);
}
}
private static String lookup(
UnitPatternProvider p,
Locale language,
char unitID,
boolean future,
boolean abbreviated,
PluralCategory category
) {
if (abbreviated && (p instanceof RelativeTimeProvider)) {
RelativeTimeProvider rp = RelativeTimeProvider.class.cast(p);
switch (unitID) {
case 'Y':
return rp.getShortYearPattern(language, future, category);
case 'M':
return rp.getShortMonthPattern(language, future, category);
case 'W':
return rp.getShortWeekPattern(language, future, category);
case 'D':
return rp.getShortDayPattern(language, future, category);
case 'H':
return rp.getShortHourPattern(language, future, category);
case 'N':
return rp.getShortMinutePattern(language, future, category);
case 'S':
return rp.getShortSecondPattern(language, future, category);
default:
throw new UnsupportedOperationException("Unit-ID: " + unitID);
}
}
switch (unitID) {
case 'Y':
return p.getYearPattern(language, future, category);
case 'M':
return p.getMonthPattern(language, future, category);
case 'W':
return p.getWeekPattern(language, future, category);
case 'D':
return p.getDayPattern(language, future, category);
case 'H':
return p.getHourPattern(language, future, category);
case 'N':
return p.getMinutePattern(language, future, category);
case 'S':
return p.getSecondPattern(language, future, category);
default:
throw new UnsupportedOperationException("Unit-ID: " + unitID);
}
}
private static String lookup(
Locale language,
TextWidth width,
int size
) {
try {
return PROVIDER.getListPattern(language, width, size);
} catch (MissingResourceException mre) { // should not happen
return FALLBACK.getListPattern(language, width, size);
}
}
//~ Innere Klassen ----------------------------------------------------
private static class FallbackProvider
implements UnitPatternProvider {
//~ Methoden ------------------------------------------------------
@Override
public String getYearPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("year", "yr", "y", width, category);
}
return getUnitPattern("y");
}
@Override
public String getMonthPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("month", "mth", "m", width, category);
}
return getUnitPattern("m");
}
@Override
public String getWeekPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("week", "wk", "w", width, category);
}
return getUnitPattern("w");
}
@Override
public String getDayPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("day", "day", "d", width, category);
}
return getUnitPattern("d");
}
@Override
public String getHourPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("hour", "hr", "h", width, category);
}
return getUnitPattern("h");
}
@Override
public String getMinutePattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("minute", "min", "m", width, category);
}
return getUnitPattern("min");
}
@Override
public String getSecondPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern("second", "sec", "s", width, category);
}
return getUnitPattern("s");
}
@Override
public String getYearPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("year", future, category);
}
return getRelativePattern("y", future);
}
@Override
public String getMonthPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("month", future, category);
}
return getRelativePattern("m", future);
}
@Override
public String getWeekPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("week", future, category);
}
return getRelativePattern("w", future);
}
@Override
public String getDayPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("day", future, category);
}
return getRelativePattern("d", future);
}
@Override
public String getHourPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("hour", future, category);
}
return getRelativePattern("h", future);
}
@Override
public String getMinutePattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("minute", future, category);
}
return getRelativePattern("min", future);
}
@Override
public String getSecondPattern(
Locale lang,
boolean future,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getRelativeEnglishPattern("second", future, category);
}
return getRelativePattern("s", future);
}
@Override
public String getNowWord(Locale lang) {
return "now";
}
private static String getEnglishPattern(
String wide,
String abbr,
String narrow,
TextWidth width,
PluralCategory category
) {
switch (width) {
case WIDE:
return getPluralPattern(wide, category);
case ABBREVIATED:
case SHORT:
return getPluralPattern(abbr, category);
case NARROW:
return "{0}" + narrow;
default:
throw new UnsupportedOperationException(width.name());
}
}
private static String getPluralPattern(
String unit,
PluralCategory category
) {
String plural = (category == PluralCategory.ONE ? "" : "s");
return "{0} " + unit + plural;
}
private static String getUnitPattern(String unit) {
return "{0} " + unit;
}
private static String getRelativeEnglishPattern(
String unit,
boolean future,
PluralCategory category
) {
String plural = (category == PluralCategory.ONE ? "" : "s");
if (future) {
return "in {0} " + unit + plural;
} else {
return "{0} " + unit + plural + " ago";
}
}
private static String getRelativePattern(
String unit,
boolean future
) {
return ((future ? "+" : "-") + "{0} " + unit);
}
@Override
public String getListPattern(
Locale lang,
TextWidth width,
int size
) {
if (size < 2) {
throw new IllegalArgumentException(
"Size must be greater than 1.");
}
StringBuilder sb = new StringBuilder(size * 5);
for (int i = 0; i < size; i++) {
sb.append('{');
sb.append(i);
sb.append('}');
if (i < size - 1) {
sb.append(", ");
}
}
return sb.toString();
}
@Override
public String getMilliPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern(
"millisecond", "msec", "ms", width, category);
}
return getUnitPattern("ms");
}
@Override
public String getMicroPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern(
"microsecond", "µsec", "µs", width, category);
}
return getUnitPattern("µs");
}
@Override
public String getNanoPattern(
Locale lang,
TextWidth width,
PluralCategory category
) {
if (lang.getLanguage().equals("en")) {
return getEnglishPattern(
"nanosecond", "nsec", "ns", width, category);
}
return getUnitPattern("ns");
}
}
}