org.joda.time.format.PeriodFormat Maven / Gradle / Ivy
Show all versions of virtdata-lib-realer Show documentation
/*
* Copyright 2001-2014 Stephen Colebourne
*
* Licensed 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.joda.time.format;
import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.joda.time.ReadWritablePeriod;
import org.joda.time.ReadablePeriod;
/**
* Factory that creates instances of PeriodFormatter.
*
* Period formatting is performed by the {@link PeriodFormatter} class.
* Three classes provide factory methods to create formatters, and this is one.
* The others are {@link ISOPeriodFormat} and {@link PeriodFormatterBuilder}.
*
* PeriodFormat is thread-safe and immutable, and the formatters it returns
* are as well.
*
* @author Brian S O'Neill
* @since 1.0
* @see ISOPeriodFormat
* @see PeriodFormatterBuilder
*/
public class PeriodFormat {
/**
* The resource bundle name.
*/
private static final String BUNDLE_NAME = "org.joda.time.format.messages";
/**
* The created formatters.
*/
private static final ConcurrentMap FORMATTERS = new ConcurrentHashMap();
/**
* Constructor.
*
* @since 1.1 (previously private)
*/
protected PeriodFormat() {
super();
}
//-----------------------------------------------------------------------
/**
* Gets the default formatter that outputs words in English.
*
* This calls {@link #wordBased(Locale)} using a locale of {@code ENGLISH}.
*
* @return the formatter, not null
*/
public static PeriodFormatter getDefault() {
return wordBased(Locale.ENGLISH);
}
/**
* Returns a word based formatter for the JDK default locale.
*
* This calls {@link #wordBased(Locale)} using the {@link Locale#getDefault() default locale}.
*
* @return the formatter, not null
* @since 2.0
*/
public static PeriodFormatter wordBased() {
return wordBased(Locale.getDefault());
}
/**
* Returns a word based formatter for the specified locale.
*
* The words are configured in a resource bundle text file -
* {@code org.joda.time.format.messages}.
* This can be added to via the normal classpath resource bundle mechanisms.
*
* You can add your own translation by creating messages_.properties file
* and adding it to the {@code org.joda.time.format.messages} path.
*
* Simple example (1 -> singular suffix, not 1 -> plural suffix):
*
*
* PeriodFormat.space=\
* PeriodFormat.comma=,
* PeriodFormat.commandand=,and
* PeriodFormat.commaspaceand=, and
* PeriodFormat.commaspace=,
* PeriodFormat.spaceandspace=\ and
* PeriodFormat.year=\ year
* PeriodFormat.years=\ years
* PeriodFormat.month=\ month
* PeriodFormat.months=\ months
* PeriodFormat.week=\ week
* PeriodFormat.weeks=\ weeks
* PeriodFormat.day=\ day
* PeriodFormat.days=\ days
* PeriodFormat.hour=\ hour
* PeriodFormat.hours=\ hours
* PeriodFormat.minute=\ minute
* PeriodFormat.minutes=\ minutes
* PeriodFormat.second=\ second
* PeriodFormat.seconds=\ seconds
* PeriodFormat.millisecond=\ millisecond
* PeriodFormat.milliseconds=\ milliseconds
*
*
*
* Some languages contain more than two suffixes. You can use regular expressions
* for them. Here's an example using regular expression for English:
*
*
* PeriodFormat.space=\
* PeriodFormat.comma=,
* PeriodFormat.commandand=,and
* PeriodFormat.commaspaceand=, and
* PeriodFormat.commaspace=,
* PeriodFormat.spaceandspace=\ and
* PeriodFormat.regex.separator=%
* PeriodFormat.years.regex=1$%.*
* PeriodFormat.years.list=\ year%\ years
* PeriodFormat.months.regex=1$%.*
* PeriodFormat.months.list=\ month%\ months
* PeriodFormat.weeks.regex=1$%.*
* PeriodFormat.weeks.list=\ week%\ weeks
* PeriodFormat.days.regex=1$%.*
* PeriodFormat.days.list=\ day%\ days
* PeriodFormat.hours.regex=1$%.*
* PeriodFormat.hours.list=\ hour%\ hours
* PeriodFormat.minutes.regex=1$%.*
* PeriodFormat.minutes.list=\ minute%\ minutes
* PeriodFormat.seconds.regex=1$%.*
* PeriodFormat.seconds.list=\ second%\ seconds
* PeriodFormat.milliseconds.regex=1$%.*
* PeriodFormat.milliseconds.list=\ millisecond%\ milliseconds
*
*
*
* You can mix both approaches. Here's example for Polish (
* "1 year, 2 years, 5 years, 12 years, 15 years, 21 years, 22 years, 25 years"
* translates to
* "1 rok, 2 lata, 5 lat, 12 lat, 15 lat, 21 lat, 22 lata, 25 lat"). Notice that
* PeriodFormat.day and PeriodFormat.days is used for day suffixes as there is no
* need for regular expressions:
*
*
* PeriodFormat.space=\
* PeriodFormat.comma=,
* PeriodFormat.commandand=,i
* PeriodFormat.commaspaceand=, i
* PeriodFormat.commaspace=,
* PeriodFormat.spaceandspace=\ i
* PeriodFormat.regex.separator=%
* PeriodFormat.years.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.years.list=\ rok%\ lata%\ lat
* PeriodFormat.months.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.months.list=\ miesi\u0105c%\ miesi\u0105ce%\ miesi\u0119cy
* PeriodFormat.weeks.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.weeks.list=\ tydzie\u0144%\ tygodnie%\ tygodni
* PeriodFormat.day=\ dzie\u0144
* PeriodFormat.days=\ dni
* PeriodFormat.hours.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.hours.list=\ godzina%\ godziny%\ godzin
* PeriodFormat.minutes.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.minutes.list=\ minuta%\ minuty%\ minut
* PeriodFormat.seconds.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.seconds.list=\ sekunda%\ sekundy%\ sekund
* PeriodFormat.milliseconds.regex=^1$%[0-9]*(?<!1)[2-4]$%[0-9]*
* PeriodFormat.milliseconds.list=\ milisekunda%\ milisekundy%\ milisekund
*
*
*
* Each PeriodFormat.<duration_field_type>.regex property stands for an array of
* regular expressions and is followed by a property
* PeriodFormat.<duration_field_type>.list holding an array of suffixes.
* PeriodFormat.regex.separator is used for splitting. See
* {@link PeriodFormatterBuilder#appendSuffix(String[], String[])} for details.
*
* Available languages are English, Danish, Dutch, French, German, Japanese,
* Polish, Portuguese and Spanish.
*
* @return the formatter, not null
* @since 2.0, regex since 2.5
*/
public static PeriodFormatter wordBased(Locale locale) {
PeriodFormatter pf = FORMATTERS.get(locale);
if (pf == null) {
DynamicWordBased dynamic = new DynamicWordBased(buildWordBased(locale));
pf = new PeriodFormatter(dynamic, dynamic, locale, null);
PeriodFormatter existing = FORMATTERS.putIfAbsent(locale, pf);
if (existing != null) {
pf = existing;
}
}
return pf;
}
//-----------------------------------------------------------------------
private static PeriodFormatter buildWordBased(Locale locale) {
ResourceBundle b = ResourceBundle.getBundle(BUNDLE_NAME, locale);
if (containsKey(b, "PeriodFormat.regex.separator")) {
return buildRegExFormatter(b, locale);
} else {
return buildNonRegExFormatter(b, locale);
}
}
private static PeriodFormatter buildRegExFormatter(ResourceBundle b, Locale locale) {
String[] variants = retrieveVariants(b);
String regExSeparator = b.getString("PeriodFormat.regex.separator");
PeriodFormatterBuilder builder = new PeriodFormatterBuilder();
builder.appendYears();
if (containsKey(b, "PeriodFormat.years.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.years.regex").split(regExSeparator),
b.getString("PeriodFormat.years.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.year"), b.getString("PeriodFormat.years"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendMonths();
if (containsKey(b, "PeriodFormat.months.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.months.regex").split(regExSeparator),
b.getString("PeriodFormat.months.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.month"), b.getString("PeriodFormat.months"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendWeeks();
if (containsKey(b, "PeriodFormat.weeks.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.weeks.regex").split(regExSeparator),
b.getString("PeriodFormat.weeks.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.week"), b.getString("PeriodFormat.weeks"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendDays();
if (containsKey(b, "PeriodFormat.days.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.days.regex").split(regExSeparator),
b.getString("PeriodFormat.days.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.day"), b.getString("PeriodFormat.days"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendHours();
if (containsKey(b, "PeriodFormat.hours.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.hours.regex").split(regExSeparator),
b.getString("PeriodFormat.hours.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.hour"), b.getString("PeriodFormat.hours"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendMinutes();
if (containsKey(b, "PeriodFormat.minutes.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.minutes.regex").split(regExSeparator),
b.getString("PeriodFormat.minutes.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.minute"), b.getString("PeriodFormat.minutes"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendSeconds();
if (containsKey(b, "PeriodFormat.seconds.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.seconds.regex").split(regExSeparator),
b.getString("PeriodFormat.seconds.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.second"), b.getString("PeriodFormat.seconds"));
}
builder.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants);
builder.appendMillis();
if (containsKey(b, "PeriodFormat.milliseconds.regex")) {
builder.appendSuffix(
b.getString("PeriodFormat.milliseconds.regex").split(regExSeparator),
b.getString("PeriodFormat.milliseconds.list").split(regExSeparator));
} else {
builder.appendSuffix(b.getString("PeriodFormat.millisecond"), b.getString("PeriodFormat.milliseconds"));
}
return builder.toFormatter().withLocale(locale);
}
private static PeriodFormatter buildNonRegExFormatter(ResourceBundle b, Locale locale) {
String[] variants = retrieveVariants(b);
return new PeriodFormatterBuilder()
.appendYears()
.appendSuffix(b.getString("PeriodFormat.year"), b.getString("PeriodFormat.years"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendMonths()
.appendSuffix(b.getString("PeriodFormat.month"), b.getString("PeriodFormat.months"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendWeeks()
.appendSuffix(b.getString("PeriodFormat.week"), b.getString("PeriodFormat.weeks"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendDays()
.appendSuffix(b.getString("PeriodFormat.day"), b.getString("PeriodFormat.days"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendHours()
.appendSuffix(b.getString("PeriodFormat.hour"), b.getString("PeriodFormat.hours"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendMinutes()
.appendSuffix(b.getString("PeriodFormat.minute"), b.getString("PeriodFormat.minutes"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendSeconds()
.appendSuffix(b.getString("PeriodFormat.second"), b.getString("PeriodFormat.seconds"))
.appendSeparator(b.getString("PeriodFormat.commaspace"), b.getString("PeriodFormat.spaceandspace"), variants)
.appendMillis()
.appendSuffix(b.getString("PeriodFormat.millisecond"), b.getString("PeriodFormat.milliseconds"))
.toFormatter().withLocale(locale);
}
private static String[] retrieveVariants(ResourceBundle b) {
return new String[] { b.getString("PeriodFormat.space"), b.getString("PeriodFormat.comma"),
b.getString("PeriodFormat.commandand"), b.getString("PeriodFormat.commaspaceand") };
}
// simulate ResourceBundle.containsKey()
private static boolean containsKey(ResourceBundle bundle, String key) {
for (Enumeration en = bundle.getKeys(); en.hasMoreElements(); ) {
if (en.nextElement().equals(key)) {
return true;
}
}
return false;
}
//-----------------------------------------------------------------------
/**
* Printer/parser that reacts to the locale and changes the word-based
* pattern if necessary.
*/
static class DynamicWordBased
implements PeriodPrinter, PeriodParser {
/** The formatter with the locale selected at construction time. */
private final PeriodFormatter iFormatter;
DynamicWordBased(PeriodFormatter formatter) {
iFormatter = formatter;
}
public int countFieldsToPrint(ReadablePeriod period, int stopAt, Locale locale) {
return getPrinter(locale).countFieldsToPrint(period, stopAt, locale);
}
public int calculatePrintedLength(ReadablePeriod period, Locale locale) {
return getPrinter(locale).calculatePrintedLength(period, locale);
}
public void printTo(StringBuffer buf, ReadablePeriod period, Locale locale) {
getPrinter(locale).printTo(buf, period, locale);
}
public void printTo(Writer out, ReadablePeriod period, Locale locale) throws IOException {
getPrinter(locale).printTo(out, period, locale);
}
private PeriodPrinter getPrinter(Locale locale) {
if (locale != null && !locale.equals(iFormatter.getLocale())) {
return wordBased(locale).getPrinter();
}
return iFormatter.getPrinter();
}
public int parseInto(
ReadWritablePeriod period, String periodStr,
int position, Locale locale) {
return getParser(locale).parseInto(period, periodStr, position, locale);
}
private PeriodParser getParser(Locale locale) {
if (locale != null && !locale.equals(iFormatter.getLocale())) {
return wordBased(locale).getParser();
}
return iFormatter.getParser();
}
}
}