All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.time4j.format.CalendarText Maven / Gradle / Ivy

There is a newer version: 4.38
Show newest version
/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2015 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (CalendarText.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.format;

import net.time4j.engine.ChronoElement;
import net.time4j.engine.Chronology;

import java.text.DateFormatSymbols;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


/**
 * 

Source for localized calendrical informations on enum basis like month * or weekday names.

* *

This class is a facade for an underlying implementation of * {@link TextProvider} which will be loaded as SPI-interface * by help of a {@code ServiceLoader}. If no such SPI-interface can be * found then this class will resort to the sources of JDK (usually * as wrapper around {@code java.text.DateFormatSymbols}).

* *

Furthermore, an instance of {@code CalendarText} can also access * the UTF-8 text resources in the folder "calendar" relative to * the class path which are not based on JDK-defaults. In this case the * presence of the i18n-module is required. In all ISO-systems the * "iso8601_{locale}.properties"-files will override the * JDK-defaults unless it is the ROOT-locale. Example:

* *

If you wish to use the name "Sonnabend" instead of the standard * word "Samstag" in german locale (english: Saturday) then you can * copy the existing file "calendar/iso8601_de.properties" from the * content of "time4j-i18n-v{version}.jar"-file into a new directory * with the same path. Then you can insert these lines extra (all seven entries * must be inserted, not just the sixth line):

* *
 *  DAY_OF_WEEK(WIDE)_1=Montag
 *  DAY_OF_WEEK(WIDE)_2=Dienstag
 *  DAY_OF_WEEK(WIDE)_3=Mittwoch
 *  DAY_OF_WEEK(WIDE)_4=Donnerstag
 *  DAY_OF_WEEK(WIDE)_5=Freitag
 *  DAY_OF_WEEK(WIDE)_6=Sonnabend
 *  DAY_OF_WEEK(WIDE)_7=Sonntag
 * 
* *

The general format of these lines is:

* *
 *  {element-name}({text-width}[|STANDALONE])_{one-based-integer}={text}
 * 
* *

STANDALONE is optional. As element name in the context of ISO-8601 * following names are supported:

* *
  • MONTH_OF_YEAR
  • *
  • QUARTER_OF_YEAR
  • *
  • DAY_OF_WEEK
  • *
  • ERA
  • *
  • AM_PM_OF_DAY
* * @author Meno Hochschild * @doctags.concurrency */ /*[deutsch] *

Quelle für lokalisierte kalendarische Informationen auf Enum-Basis * wie zum Beispiel Monats- oder Wochentagsnamen.

* *

Diese Klasse ist eine Fassade für eine dahinterstehende * {@link TextProvider}-Implementierung, die als SPI-Interface * über einen {@code ServiceLoader}-Mechanismus geladen wird. Gibt es * keine solche Implementierung, wird intern auf die Quellen des JDK mittels * der Schnittstelle {@code java.text.DateFormatSymbols} ausgewichen.

* *

Darüberhinaus kann eine Instanz von {@code CalendarText} auch * auf UTF-8-Textressourcen im Verzeichnis "calendar" innerhalb des * Klassenpfads zugreifen, die nicht auf JDK-Vorgaben beruhen. In diesem Fall * ist das i18n-Modul notwendig. Für alle ISO-Systeme gilt, daß die * Einträge in den Dateien "iso8601_{locale}.properties" die * JDK-Vorgaben überschreiben, sofern es nicht die ROOT-locale ist. * Beispiel:

* *

Wenn der Name "Sonnabend" anstatt der Standardvorgabe * "Samstag" in der deutschen Variante verwendet werden soll, * dann kann die existierende Datei "calendar/iso8601_de.properties" * vom Inhalt der Bibliotheksdatei "time4j-i18n-v{version].jar" * in ein neues Verzeichnis mit dem gleichen Pfad kopiert werden. Danach * können alle folgenden Zeilen extra eingefügt werden (nicht nur * die sechste Zeile allein):

* *
 *  DAY_OF_WEEK(WIDE)_1=Montag
 *  DAY_OF_WEEK(WIDE)_2=Dienstag
 *  DAY_OF_WEEK(WIDE)_3=Mittwoch
 *  DAY_OF_WEEK(WIDE)_4=Donnerstag
 *  DAY_OF_WEEK(WIDE)_5=Freitag
 *  DAY_OF_WEEK(WIDE)_6=Sonnabend
 *  DAY_OF_WEEK(WIDE)_7=Sonntag
 * 
* *

Das allgemeine Format dieser Zeilen ist:

* *
 *  {element-name}({text-width}[|STANDALONE])_{eins-basierter-integer}={text}
 * 
* *

STANDALONE ist optional. Als Elementname im Kontext von ISO-8601 werden * folgende Namen unterstützt:

* *
  • MONTH_OF_YEAR
  • *
  • QUARTER_OF_YEAR
  • *
  • DAY_OF_WEEK
  • *
  • ERA
  • *
  • AM_PM_OF_DAY
* * @author Meno Hochschild * @doctags.concurrency */ public final class CalendarText { //~ Statische Felder/Initialisierungen -------------------------------- /** *

Default calendar type for all ISO systems.

*/ /*[deutsch] *

Standard-Kalendertyp für alle ISO-Systeme.

*/ public static final String ISO_CALENDAR_TYPE = "iso8601"; private static final ConcurrentMap CACHE = new ConcurrentHashMap(); //~ Instanzvariablen -------------------------------------------------- // Name des Provider private final String provider; // Standardtexte private final Map> stdMonths; private final Map> leapMonths; private final Map> quarters; private final Map> weekdays; private final Map eras; private final Map meridiems; // Textformen spezifisch für eine Chronologie private final ResourceBundle textForms; private final MissingResourceException mre; //~ Konstruktoren ----------------------------------------------------- private CalendarText( String calendarType, Locale locale, TextProvider p ) { super(); this.provider = p.toString(); // Monate, Quartale, Wochentage, Äras und AM/PM this.stdMonths = Collections.unmodifiableMap( getMonths(calendarType, locale, p, false)); Map> tmpLeapMonths = getMonths(calendarType, locale, p, true); if (tmpLeapMonths == null) { this.leapMonths = this.stdMonths; } else { this.leapMonths = Collections.unmodifiableMap(tmpLeapMonths); } Map> qt = new EnumMap> (TextWidth.class); for (TextWidth tw : TextWidth.values()) { Map qo = new EnumMap(OutputContext.class); for (OutputContext oc : OutputContext.values()) { qo.put( oc, new TextAccessor( p.quarters(calendarType, locale, tw, oc), locale)); } qt.put(tw, qo); } this.quarters = Collections.unmodifiableMap(qt); Map> wt = new EnumMap> (TextWidth.class); for (TextWidth tw : TextWidth.values()) { Map wo = new EnumMap(OutputContext.class); for (OutputContext oc : OutputContext.values()) { wo.put( oc, new TextAccessor( p.weekdays(calendarType, locale, tw, oc), locale)); } wt.put(tw, wo); } this.weekdays = Collections.unmodifiableMap(wt); Map et = new EnumMap(TextWidth.class); for (TextWidth tw : TextWidth.values()) { et.put( tw, new TextAccessor(p.eras(calendarType, locale, tw), locale)); } this.eras = Collections.unmodifiableMap(et); Map mt = new EnumMap(TextWidth.class); for (TextWidth tw : TextWidth.values()) { mt.put( tw, new TextAccessor( p.meridiems(calendarType, locale, tw), locale)); } this.meridiems = Collections.unmodifiableMap(mt); // Allgemeine Textformen als optionales Bundle vorbereiten // Wichtig: Letzter Schritt im Konstruktor wg. Bundle-Cache ResourceBundle rb = null; MissingResourceException tmpMre = null; try { rb = ResourceBundle.getBundle( "calendar/" + calendarType, locale, getLoader(), p.getControl()); } catch (MissingResourceException ex) { tmpMre = ex; } this.textForms = rb; this.mre = tmpMre; } //~ Methoden ---------------------------------------------------------- /** *

Returns an instance of {@code CalendarText} for given chronology * and language.

* * @param chronology chronology (with calendar system) * @param locale language * @return {@code CalendarText} object maybe cached */ /*[deutsch] *

Gibt eine Instanz dieser Klasse für die angegebene Chronologie * und Sprache zurück.

* * @param chronology chronology (with calendar system) * @param locale language * @return {@code CalendarText} object maybe cached */ public static CalendarText getInstance( Chronology chronology, Locale locale ) { return getInstance(extractCalendarType(chronology), locale); } /** *

Returns an instance of {@code CalendarText} for given calendar type * and language.

* * @param calendarType name of calendar system * @param locale language * @return {@code CalendarText} object maybe cached * @see CalendarType */ /*[deutsch] *

Gibt eine Instanz dieser Klasse für Kalendertyp * und Sprache zurück.

* * @param calendarType name of calendar system * @param locale language * @return {@code CalendarText} object maybe cached * @see CalendarType */ public static CalendarText getInstance( String calendarType, Locale locale ) { if (calendarType == null) { throw new NullPointerException("Missing calendar type."); } StringBuilder sb = new StringBuilder(); sb.append(calendarType); sb.append(':'); sb.append(locale.getLanguage()); String country = locale.getCountry(); if (!country.isEmpty()) { sb.append('-'); sb.append(country); } String key = sb.toString(); CalendarText instance = CACHE.get(key); if (instance == null) { TextProvider p = null; ClassLoader c = getLoader(); // ServiceLoader-Mechanismus (Suche nach externen Providern) for (TextProvider tmp : ServiceLoader.load(TextProvider.class, c)) { if ( isCalendarTypeSupported(tmp, calendarType) && isLocaleSupported(tmp, locale) ) { p = tmp; break; } } // Java-Ressourcen if (p == null) { // TODO: Für Java 8 neuen Provider definieren (mit Quartalen)? TextProvider tmp = new JDKTextProvider(); if ( isCalendarTypeSupported(tmp, calendarType) && isLocaleSupported(tmp, locale) ) { p = tmp; } if (p == null) { p = new FallbackProvider(); // keine-ISO-Ressource } } instance = new CalendarText(calendarType, locale, p); CalendarText old = CACHE.putIfAbsent(key, instance); if (old != null) { instance = old; } } return instance; } /** *

Yields an {@code Accessor} for all standard months.

* *

The underlying list is sorted such that it will obey to the * typical order of months in given calendar system. ISO-systems * define January as first month and at whole 12 months. Other * calendar systems can also define for example 13 months. The order * of element value enums must be in agreement with the order of * the text forms contained here.

* *

The default implementation handles SHORT as synonym for * ABBREVIATED in the context of ISO-8601.

* * @param textWidth text width of displayed month name * @param outputContext output context (stand-alone?) * @return accessor for standard month names * @see net.time4j.Month */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Standard-Monatsnamen.

* *

Die Liste ist so sortiert, daß die für das jeweilige * Kalendersystem typische Reihenfolge der Monate eingehalten wird. * ISO-Systeme definieren den Januar als den ersten Monat und insgesamt * 12 Monate. Andere Kalendersysteme können auch 13 Monate definieren. * Die Reihenfolge der Elementwert-Enums muß mit der Reihenfolge der * hier enthaltenen Textformen übereinstimmen.

* *

Speziell für ISO-8601 behandelt die Standardimplementierung * die Textbreiten SHORT und ABBREVIATED gleich.

* * @param textWidth text width of displayed month name * @param outputContext output context (stand-alone?) * @return accessor for standard month names * @see net.time4j.Month */ public TextAccessor getStdMonths( TextWidth textWidth, OutputContext outputContext ) { return this.getMonths(textWidth, outputContext, false); } /** *

Yields an {@code Accessor} for all months if a leap month * is relevant.

* *

Note: Leap months are defined in some calendar systems like the * hebrew calendar ("Adar II") else there is no difference * between standard and leap months escpecially not in ISO-8601.

* * @param textWidth text width of displayed month name * @param outputContext output context (stand-alone?) * @return accessor for month names * @see net.time4j.Month * @see #getStdMonths(TextWidth, OutputContext) */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Monatsnamen, wenn ein Schaltmonat relevant ist.

* *

Hinweis: Schaltmonate sind in einigen Kalendersystemen wie dem * hebräischen Kalender definiert ("Adar II"). Ansonsten * gibt es keinen Unterschied zwischen Standard- und Schaltmonaten, * insbesondere nicht im ISO-8601-Standard.

* * @param textWidth text width of displayed month name * @param outputContext output context (stand-alone?) * @return accessor for month names * @see net.time4j.Month * @see #getStdMonths(TextWidth, OutputContext) */ public TextAccessor getLeapMonths( TextWidth textWidth, OutputContext outputContext ) { return this.getMonths(textWidth, outputContext, true); } /** *

Yields an {@code Accessor} for all quarter years.

* *

The underlying list of text forms is sorted in the same order * as the enum {@code Quarter} and uses its ordinal index as list * index. ISO systems define the range January-March as first quarter * etc. and at whole four quarters per calendar year.

* *

The default implementation handles SHORT as synonym for * ABBREVIATED in the context of ISO-8601.

* * @param textWidth text width of displayed quarter name * @param outputContext output context (stand-alone?) * @return accessor for quarter names * @see net.time4j.Quarter */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Quartalsnamen.

* *

Die Liste ist wie das Enum {@code Quarter} sortiert und verwendet * dessen Ordinalindex als Listenindex. ISO-Systeme definieren den * Zeitraum Januar-März als erstes Quartal usw. und insgesamt * 4 Quartale pro Kalenderjahr.

* *

Speziell für ISO-8601 behandelt die Standardimplementierung * die Textbreiten SHORT und ABBREVIATED gleich.

* * @param textWidth text width of displayed quarter name * @param outputContext output context (stand-alone?) * @return accessor for quarter names * @see net.time4j.Quarter */ public TextAccessor getQuarters( TextWidth textWidth, OutputContext outputContext ) { return this.quarters.get(textWidth).get(outputContext); } /** *

Yields an {@code Accessor} for all weekday names.

* *

The underlying list of text forms is sorted such that the * typical order of weekdays is used in given calendar system. * ISO systems define Monday as first day of week and at whole * 7 weekdays. This order is also valid for US in the context of * this class although in US Sunday is considered as start of a * week. The order element value enums must be in agreement with * the order of text forms contained here.

* * @param textWidth text width of displayed weekday name * @param outputContext output context (stand-alone?) * @return accessor for weekday names * @see net.time4j.Weekday */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Wochentagsnamen.

* *

Die Liste ist so sortiert, daß die für das jeweilige * Kalendersystem typische Reihenfolge der Wochentage eingehalten wird. * ISO-Systeme definieren den Montag als den ersten Wochentag und insgesamt * 7 Wochentage. Diese Sortierung gilt im Kontext dieser Klasse auch * für die USA, in denen der Sonntag als erster Tag der Woche gilt. * Die Reihenfolge der Elementwert-Enums muß mit der Reihenfolge * der hier enthaltenen Textformen übereinstimmen.

* * @param textWidth text width of displayed weekday name * @param outputContext output context (stand-alone?) * @return accessor for weekday names * @see net.time4j.Weekday */ public TextAccessor getWeekdays( TextWidth textWidth, OutputContext outputContext ) { return this.weekdays.get(textWidth).get(outputContext); } /** *

Yields an {@code Accessor} for all era names.

* *

The underlying list of text forms is sorted such that the * typical order of eras is used in given calendar system. ISO systems * define era names based on their historical extensions (eras of * gregorian/historic calendar) because they themselves have no internal * concept of eras. The order of element value enums must be in agreement * with the text forms contained here. If an era is not defined on enum * basis then the format API will not evaluate this class but the * {@code CalendarSystem} to get the right text forms.

* * @param textWidth text width of displayed era name * @return accessor for era names * @see net.time4j.engine.CalendarSystem#getEras() */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Äranamen.

* *

Die Liste ist so sortiert, daß die für das jeweilige * Kalendersystem typische Reihenfolge der Äranamen eingehalten wird. * ISO-Systeme definieren Äranamen basierend auf ihren historischen * Erweiterungen, da sie selbst keine kennen (also die des gregorianischen * historischen Kalenders). Die Reihenfolge der Elementwert-Enums muß * mit der Reihenfolge der hier enthaltenen Textformen übereinstimmen. * Wenn eine Ära nicht auf Enum-Basis definiert ist, wertet das * Format-API nicht diese Klasse, sondern das {@code CalendarSystem} zur * Bestimmung der Textformen aus.

* * @param textWidth text width of displayed era name * @return accessor for era names * @see net.time4j.engine.CalendarSystem#getEras() */ public TextAccessor getEras(TextWidth textWidth) { return this.eras.get(textWidth); } /** *

Yields an {@code Accessor} for all am/pm-names.

* *

The underlying list of text forms is sorted in AM-PM-order. * The order of element value enums must be the same.

* * @param textWidth text width of displayed AM/PM name * @return accessor for AM/PM names * @see net.time4j.Meridiem */ /*[deutsch] *

Liefert einen {@code Accessor} für alle * Tagesabschnittsnamen.

* *

Die Liste ist in AM/PM-Reihenfolge sortiert. Die Reihenfolge der * Elementwert-Enums muß mit der Reihenfolge der hier enthaltenen * Textformen übereinstimmen.

* * @param textWidth text width of displayed AM/PM name * @return accessor for AM/PM names * @see net.time4j.Meridiem */ public TextAccessor getMeridiems(TextWidth textWidth) { return this.meridiems.get(textWidth); } /** *

Yields an {@code Accessor} for all text forms of given * chronological element.

* *

Text forms might exist in different variations. In case of * enum-based variants the name of the enum (example "WIDE" in * the variant {@code TextWidth}) is to be used, in case of boolean-based * variants the literals "true" and "false" are to be * used.

* *

While the methods {@code getStdMonths()}, {@code getWeekdays()} * etc.are mainly based on JDK-defaults, this method is escpecially * designed for querying chronological texts which are not contained in * JDK. Text forms will be stored internally in the resource folder * "calendar" relative to class path in properties-files using * UTF-8 encoding. The basic name of these resources is the calendar type. * The combination of element name and optionally variants in the form * "(variant1|variant2|...|variantN)" and the underscore and * finally a numerical suffix with base 1 serves as resource text key. * If there is no entry for given key in the resources then this method * will simply yield the name of enum value associated with given element * value.

* * @param generic type of element values based on enums * @param element element text forms are searched for * @param variants text form variants (optional) * @return accessor for any text forms * @throws MissingResourceException if for given calendar type there are * no text resource files */ /*[deutsch] *

Liefert einen {@code Accessor} für alle Textformen des * angegebenen chronologischen Elements.

* *

Textformen können unter Umständen in verschiedenen * Varianten vorkommen. Als Variantenbezug dient bei enum-Varianten * der Name der Enum-Ausprägung (Beispiel "WIDE" in * der Variante {@code TextWidth}), im boolean-Fall sind die Literale * "true" und "false" zu verwenden.

* *

Während {@code getStdMonths()}, {@code getWeekdays()} etc. in * erster Linie auf JDK-Vorgaben beruhen, dient diese Methode dazu, * chronologiespezifische Texte zu beschaffen, die nicht im JDK enthalten * sind. Textformen werden intern im Verzeichnis "calendar" * des Klassenpfads mit Hilfe von properties-Dateien im UTF-8-Format * gespeichert. Der Basisname dieser Ressourcen ist der Kalendertyp. Als * Textschluuml;ssel dient die Kombination aus Elementname, optional * Varianten in der Form "(variant1|variant2|...|variantN)", * dem Unterstrich und schließlich einem numerischen Suffix mit * Basis 1. Wird in den Ressourcen zum angegebenen Schlüssel kein * Eintrag gefunden, liefert diese Methode einfach den Namen des mit dem * Element assoziierten enum-Werts.

* * @param generic type of element values based on enums * @param element element text forms are searched for * @param variants text form variants (optional) * @return accessor for any text forms * @throws MissingResourceException if for given calendar type there are * no text resource files */ public > TextAccessor getTextForms( ChronoElement element, String... variants ) { if (this.textForms == null) { throw new MissingResourceException( this.mre.getMessage(), this.mre.getClassName(), this.mre.getKey()); } V[] enums = element.getType().getEnumConstants(); int len = enums.length; String[] tfs = new String[len]; StringBuilder sb = new StringBuilder(element.name()); if ( (variants != null) && (variants.length > 0) ) { boolean first = true; for (int v = 0; v < variants.length; v++) { if (first) { sb.append('('); first = false; } else { sb.append('|'); } sb.append(variants[v]); } sb.append(')'); } String raw = sb.toString(); for (int i = 0; i < len; i++) { String vkey = toKey(raw, i); if (this.textForms.containsKey(vkey)) { tfs[i] = this.textForms.getString(vkey); } else { String skey = toKey(element.name(), i); if (this.textForms.containsKey(skey)) { tfs[i] = this.textForms.getString(skey); } else { tfs[i] = enums[i].name(); } } } return new TextAccessor(tfs, this.textForms.getLocale()); } /** *

Yields the localized GMT-prefix which is used in the * localized GMT format of CLDR.

* *

Note that using the GMT-notation is at least old-fashioned. * Users should prefer the UTC-notation as combination of the * "UTC"-literal and an ISO-8601-timezone-offset. This * method is mainly a fit to the CLDR data.

* * @param locale language and country configuration * @return localized GMT-String defaults to "GMT" */ /*[deutsch] *

Liefert das lokalisierte GMT-Präfix, das im * localized GMT format von CLDR benutzt wird.

* *

Hinweis: Die GMT-Schreibweise ist mindestens veraltet. Anwender * sollten die UTC-Schreibweise als Kombination des Literals * "UTC" mit einem ISO-8601-Zeitzonen-Offset bevorzugen. * Diese Methode ist in erster Linie eine Anpassung an die * CLDR-Daten.

* * @param locale language and country configuration * @return localized GMT-String defaults to "GMT" */ public static String getGMTPrefix(Locale locale) { CalendarText ct = CalendarText.getInstance(ISO_CALENDAR_TYPE, locale); if (ct.textForms == null) { return "GMT"; } return ct.textForms.getString("prefixGMTOffset"); } /** *

Yields the name of the internal {@link TextProvider}.

*/ /*[deutsch] *

Liefert den Namen des internen {@link TextProvider}.

*/ @Override public String toString() { return this.provider; } /** *

Clears the internal cache.

* *

This method should be called if the internal text resources have * changed and must be reloaded with a suitable {@code ClassLoader}.

*/ /*[deutsch] *

Löscht den internen Cache.

* *

Diese Methode sollte aufgerufen werden, wenn sich die internen * Text-Ressourcen geändert haben und mit einem geeigneten * {@code ClassLoader} neu geladen werden müssen.

*/ public static void clearCache() { CACHE.clear(); } /** *

Extrahiert den Kalendertyp aus der angegebenen Chronologie.

* *

Kann kein Kalendertyp ermittelt werden, wird {@code ISO_CALENDAR_TYPE} * als Ausweichoption zurückgegeben.

* * @param chronology chronology to be evaluated * @return calendar type, never {@code null} */ static String extractCalendarType(Chronology chronology) { CalendarType ft = chronology.getChronoType().getAnnotation(CalendarType.class); return ((ft == null) ? ISO_CALENDAR_TYPE : ft.value()); } private TextAccessor getMonths( TextWidth textWidth, OutputContext outputContext, boolean leapForm ) { if (leapForm) { return this.leapMonths.get(textWidth).get(outputContext); } else { return this.stdMonths.get(textWidth).get(outputContext); } } private static Map> getMonths( String calendarType, Locale locale, TextProvider p, boolean leapForm ) { Map> mt = new EnumMap> (TextWidth.class); boolean usesDifferentLeapForm = false; for (TextWidth tw : TextWidth.values()) { Map mo = new EnumMap(OutputContext.class); for (OutputContext oc : OutputContext.values()) { String[] ls = p.months(calendarType, locale, tw, oc, leapForm); if (leapForm && !usesDifferentLeapForm) { String[] std = p.months(calendarType, locale, tw, oc, false); usesDifferentLeapForm = !Arrays.equals(std, ls); } mo.put(oc, new TextAccessor(ls, locale)); } mt.put(tw, mo); } return ((!leapForm || usesDifferentLeapForm) ? mt : null); } private static boolean isCalendarTypeSupported( TextProvider p, String calendarType ) { for (String c : p.getSupportedCalendarTypes()) { if (c.equals(calendarType)) { return true; } } return false; } private static boolean isLocaleSupported( TextProvider p, Locale locale ) { String lang = locale.getLanguage(); for (Locale l : p.getAvailableLocales()) { if (lang.equals(l.getLanguage())) { return true; } } return false; } private static String toKey( String raw, int counter ) { StringBuilder keyBuilder = new StringBuilder(raw); keyBuilder.append('_'); keyBuilder.append(counter + 1); return keyBuilder.toString(); } private static ClassLoader getLoader() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = CalendarText.class.getClassLoader(); } if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } return cl; } //~ Innere Klassen ---------------------------------------------------- private static class JDKTextProvider implements TextProvider { //~ Methoden ------------------------------------------------------ @Override public String[] getSupportedCalendarTypes() { return new String[] { ISO_CALENDAR_TYPE }; } @Override public Locale[] getAvailableLocales() { return DateFormatSymbols.getAvailableLocales(); } @Override public String[] months( String calendarType, Locale locale, TextWidth tw, OutputContext oc, boolean leapForm ) { DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale); switch (tw) { case WIDE: return dfs.getMonths(); case ABBREVIATED: case SHORT: return dfs.getShortMonths(); case NARROW: return narrow(dfs.getShortMonths(), 12); default: throw new UnsupportedOperationException(tw.name()); } } @Override public String[] quarters( String calendarType, Locale locale, TextWidth tw, OutputContext oc ) { return new String[] {"Q1", "Q2", "Q3", "Q4"}; // fallback } @Override public String[] weekdays( String calendarType, Locale locale, TextWidth tw, OutputContext oc ) { DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale); String[] result; switch (tw) { case WIDE: result = dfs.getWeekdays(); // 8 Elemente break; case ABBREVIATED: case SHORT: result = dfs.getShortWeekdays(); // 8 Elemente break; case NARROW: String[] names = // 7 Elemente weekdays("", locale, TextWidth.SHORT, oc); result = narrow(names, 7); break; default: throw new UnsupportedOperationException( "Unknown text width: " + tw); } if (result.length > 7) { // ISO-Reihenfolge erzwingen String sunday = result[1]; String[] arr = new String[7]; for (int i = 2; i < 8; i++) { arr[i - 2] = result[i]; } arr[6] = sunday; result = arr; } return result; } @Override public String[] eras( String calendarType, Locale locale, TextWidth textWidth ) { DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale); if (textWidth == TextWidth.NARROW) { String[] eras = dfs.getEras(); String[] ret = new String[eras.length]; for (int i = 0, n = eras.length; i < n; i++) { if (!eras[i].isEmpty()) { ret[i] = toSingleLetter(eras[i]); } else if ((i == 0) && (eras.length == 2)) { ret[i] = "B"; } else if ((i == 1) && (eras.length == 2)) { ret[i] = "A"; } else { ret[i] = String.valueOf(i); } } return ret; } else { return dfs.getEras(); } } @Override public String[] meridiems( String calendarType, Locale locale, TextWidth textWidth ) { if (textWidth == TextWidth.NARROW) { return new String[] {"A", "P"}; } // JDK-Quelle return DateFormatSymbols.getInstance(locale).getAmPmStrings(); } @Override public ResourceBundle.Control getControl() { return ResourceBundle.Control.getNoFallbackControl( ResourceBundle.Control.FORMAT_DEFAULT); } @Override public String toString() { return "JDKTextProvider"; } private static String[] narrow( String[] names, int len ) { String[] ret = new String[len]; for (int i = 0; i < len; i++) { if (!names[i].isEmpty()) { ret[i] = toSingleLetter(names[i]); } else { ret[i] = String.valueOf(i + 1); } } return ret; } private static String toSingleLetter(String input) { // diakritische Zeichen entfernen char c = Normalizer.normalize(input, Normalizer.Form.NFD).charAt(0); if ((c >= 'A') && (c <= 'Z')) { return String.valueOf(c); } else if ((c >= 'a') && (c <= 'z')) { c += ('A' - 'a'); return String.valueOf(c); } else if ((c >= '\u0410') && (c <= '\u042F')) { // kyrillisch (ru) return String.valueOf(c); } else if ((c >= '\u0430') && (c <= '\u044F')) { // kyrillisch (ru) c += ('\u0410' - '\u0430'); return String.valueOf(c); } else { return input; // NARROW-Form nicht möglich => nichts ändern! } } } private static class FallbackProvider implements TextProvider { //~ Methoden ------------------------------------------------------ @Override public String[] getSupportedCalendarTypes() { throw new UnsupportedOperationException("Never called."); } @Override public Locale[] getAvailableLocales() { throw new UnsupportedOperationException("Never called."); } @Override public String[] months( String calendarType, Locale locale, TextWidth textWidth, OutputContext outputContext, boolean leapForm ) { if (textWidth == TextWidth.WIDE) { return new String[] { "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13"}; } else { return new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}; } } @Override public String[] quarters( String calendarType, Locale locale, TextWidth textWidth, OutputContext outputContext ) { if (textWidth == TextWidth.NARROW) { return new String[] {"1", "2", "3", "4"}; } else { return new String[] {"Q1", "Q2", "Q3", "Q4"}; } } @Override public String[] weekdays( String calendarType, Locale locale, TextWidth textWidth, OutputContext outputContext ) { return new String[] {"1", "2", "3", "4", "5", "6", "7"}; } @Override public String[] eras( String calendarType, Locale locale, TextWidth textWidth ) { if (textWidth == TextWidth.NARROW) { return new String[] {"B", "A"}; } else { return new String[] {"BC", "AD"}; } } @Override public String[] meridiems( String calendarType, Locale locale, TextWidth textWidth ) { if (textWidth == TextWidth.NARROW) { return new String[] {"A", "P"}; } else { return new String[] {"AM", "PM"}; } } @Override public ResourceBundle.Control getControl() { return ResourceBundle.Control.getNoFallbackControl( ResourceBundle.Control.FORMAT_DEFAULT); } @Override public String toString() { return "FallbackProvider"; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy