/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (Attributes.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 <http://www.gnu.org/licenses/>. * ----------------------------------------------------------------------- */ package net.time4j.format; import net.time4j.engine.AttributeKey; import net.time4j.engine.AttributeQuery; import net.time4j.engine.Chronology; import net.time4j.engine.StartOfDay; import net.time4j.tz.TZID; import net.time4j.tz.Timezone; import net.time4j.tz.TransitionStrategy; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; /** * <p>A collection of format attributes for controlling the formatting * and parsing. </p> * * @author Meno Hochschild * @doctags.concurrency {immutable} */ /*[deutsch] * <p>Formatattribute zum Steuern des Format- und Interpretierungsvorgangs. </p> * * @author Meno Hochschild * @doctags.concurrency {immutable} */ public final class Attributes implements AttributeQuery { //~ Statische Felder/Initialisierungen -------------------------------- /** * <p>Attribute for the calendar type. </p> * * <p>This attribute is effectively read-only and usually derived * from the corresponding annotation value of any given chronology. * Default value: {@link CalendarText#ISO_CALENDAR_TYPE} </p> * * @see CalendarType */ /*[deutsch] * <p>Gibt den Kalendertyp an. </p> * * <p>Dieses Attribut ist effektiv nur mit Lesezugriff und wird aus * der Annotation einer Chronologie abgeleitet. Standardwert: * {@link CalendarText#ISO_CALENDAR_TYPE} </p> * * @see CalendarType */ public static final AttributeKey<String> CALENDAR_TYPE = PredefinedKey.valueOf("CALENDAR_TYPE", String.class); /** * <p>Attribute controlling the language output and parsing of * chronological texts (for example month names). </p> * * <p>Default value: {@code Locale.ROOT}. </p> */ /*[deutsch] * <p>Gibt die Sprach- und Ländereinstellung an, die die * Sprachausgabe von chronologischen Texten (Beispiel Monatsnamen) * steuert. </p> * * <p>Standardwert: {@code Locale.ROOT}. </p> */ public static final AttributeKey<Locale> LANGUAGE = PredefinedKey.valueOf("LANGUAGE", Locale.class); /** * <p>Attribute denoting the timezone identifier for display purposes. </p> * * <p>When printing a global type this attribute controls the zonal * representation. If this attribute is missing then Time4J will throw * an exception because the internal timezone reference UTC+00:00 of * global types is not intended to be used for display purposes. </p> * * <p>When parsing a global type this attribute serves as replacement * timezone if the parsing has not recognized any timezone or offset * information in the text to be parsed. If the attribute is also missing * then Time4J will throw an exception. </p> * * <p><i>Note that before version v2.0 the behaviour of Time4J was * different. When printing, the default {@code ZonalOffset.UTC} was used. * When parsing, the system default timezone was used as default in case * of missing attribute and lax mode.</i> </p> */ /*[deutsch] * <p>Gibt die Zeitzonen-ID für die Verwendung in formatierten * Darstellungen an. </p> * * <p>In der Textausgabe von globalen Typen kontrolliert dieses Attribut * die zonale Darstellung. Fehlt das Attribut, wird Time4J eine Ausnahme * werfen, weil der interne Zeitzonenbezug UTC+00:00 von globalen Typen * nicht für Anzeigezwecke gedacht ist. </p> * * <p>Wenn umgekehrt ein Text als globaler Typ interpretiert werden soll, * dient dieses Attribut als Ersatzwert, falls beim Parsen im Text keine * Zeitzone und auch kein Offset erkannt werden konnten. Fehlt auch hier * das Attribut, wird eine Ausnahme geworfen. </p> * * <p><i>Hinweis: Vor Version v2.0 war das Verhalten von Time4J anders. * In der Textausgabe war der Standard immer {@code ZonalOffset.UTC}. * Beim Parsen war die Systemzeitzone die Vorgabe im laxen Modus * gewesen, bevor eine Ausnahme flog.</i> </p> */ public static final AttributeKey<TZID> TIMEZONE_ID = PredefinedKey.valueOf("TIMEZONE_ID", TZID.class); /** * <p>Attribute for the conflict strategy to be used in resolving * ambivalent or invalid local timestamps. </p> * * <p>If this attribute is missing then Time4J will assume the default * conflict strategy. </p> * * @see net.time4j.tz.Timezone#DEFAULT_CONFLICT_STRATEGY */ /*[deutsch] * <p>Gibt die Konfliktstrategie an, die bei der Auflösung von nicht * eindeutigen lokalen Zeitstempeln zu verwenden ist. </p> * * <p>Fehlt das Attribut, wird eine Standardstrategie angenommen. </p> * * @see net.time4j.tz.Timezone#DEFAULT_CONFLICT_STRATEGY */ public static final AttributeKey<TransitionStrategy> TRANSITION_STRATEGY = PredefinedKey.valueOf("TRANSITION_STRATEGY", TransitionStrategy.class); /** * <p>Attribute which controls the leniency in parsing. </p> * * <p>Setting of this attribute also changes other attributes: </p> * * <table border="1" style="margin-top:5px;"> * <caption>Legend</caption> * <tr> * <th>LENIENCY</th> * <th>{@link #PARSE_CASE_INSENSITIVE}</th> * <th>{@link #PARSE_PARTIAL_COMPARE}</th> * <th>{@link #TRAILING_CHARACTERS}</th> * <th>{@link #PARSE_MULTIPLE_CONTEXT}</th> * </tr> * <tr><td>STRICT</td><td>false</td><td>false</td><td>false</td><td>false</td></tr> * <tr><td>SMART</td><td>true</td><td>false</td><td>false</td><td>true</td></tr> * <tr><td>LAX</td><td>true</td><td>true</td><td>true</td><td>true</td></tr> * </table> * * <p>Default value: {@link Leniency#SMART} </p> */ /*[deutsch] * <p>Legt den Nachsichtigkeitsmodus beim Parsen fest. </p> * * <p>Das Setzen dieses Attributs beeinflußt auch andere * Attribute: </p> * * <table border="1" style="margin-top:5px;"> * <caption>Legende</caption> * <tr> * <th>LENIENCY</th> * <th>{@link #PARSE_CASE_INSENSITIVE}</th> * <th>{@link #PARSE_PARTIAL_COMPARE}</th> * <th>{@link #TRAILING_CHARACTERS}</th> * <th>{@link #PARSE_MULTIPLE_CONTEXT}</th> * </tr> * <tr><td>STRICT</td><td>false</td><td>false</td><td>false</td><td>false</td></tr> * <tr><td>SMART</td><td>true</td><td>false</td><td>false</td><td>true</td></tr> * <tr><td>LAX</td><td>true</td><td>true</td><td>true</td><td>true</td></tr> * </table> * * <p>Standardwert: {@link Leniency#SMART} </p> */ public static final AttributeKey<Leniency> LENIENCY = PredefinedKey.valueOf("LENIENCY", Leniency.class); /** * <p>Determines the text width to be used in formatting and parsing. </p> * * <p>Default value: {@link TextWidth#WIDE} </p> */ /*[deutsch] * <p>Gibt die verwendete Textbreite an. </p> * * <p>Standardwert: {@link TextWidth#WIDE} </p> */ public static final AttributeKey<TextWidth> TEXT_WIDTH = PredefinedKey.valueOf("TEXT_WIDTH", TextWidth.class); /** * <p>Determines the output context to be used in formatting and * parsing. </p> * * <p>Default value: {@link OutputContext#FORMAT} </p> */ /*[deutsch] * <p>Gibt den verwendeten Ausgabekontext an. </p> * * <p>Standardwert: {@link OutputContext#FORMAT} </p> */ public static final AttributeKey<OutputContext> OUTPUT_CONTEXT = PredefinedKey.valueOf("OUTPUT_CONTEXT", OutputContext.class); /** * <p>This attribute controls if the case of text is irrelevant * in parsing or not. </p> * * <p>Default value: {@code true} </p> */ /*[deutsch] * <p>Steuert, ob beim Parsen die Groß- und Kleinschreibung * außer Acht gelassen werden soll. </p> * * <p>Standardwert: {@code true} </p> */ public static final AttributeKey<Boolean> PARSE_CASE_INSENSITIVE = PredefinedKey.valueOf("PARSE_CASE_INSENSITIVE", Boolean.class); /** * <p>This attribute controls if the parser will only check the * start of a chronological text. </p> * * <p>Abbreviations can be parsed by help of this attribute, too. * Default value: {@code false} </p> */ /*[deutsch] * <p>Steuert, ob beim Parsen nur Textanfänge geprüft werden * sollen. </p> * * <p>Mit diesem Attribut können auch Abkürzungen noch * sinnvoll interpretiert werden. Standardwert: {@code false} </p> */ public static final AttributeKey<Boolean> PARSE_PARTIAL_COMPARE = PredefinedKey.valueOf("PARSE_PARTIAL_COMPARE", Boolean.class); /** * <p>This attribute controls if the parser will also try alternative * output contexts if parsing with the original one fails. </p> * * <p>Relates to month names, weekday names and quarter names. * Default value: {@code true} </p> * * @see OutputContext * @see #OUTPUT_CONTEXT * @since 3.14/4.11 */ /*[deutsch] * <p>Steuert, ob beim Parsen auch alternative Ausgabekontexte geprüft werden, * wenn der ursprünglich angenommene Kontext fehlschlägt. </p> * * <p>Bezieht sich auf Monatsnamen, Wochentagsnamen und Quartalsnamen. * Standardwert: {@code true} </p> * * @see OutputContext * @see #OUTPUT_CONTEXT * @since 3.14/4.11 */ public static final AttributeKey<Boolean> PARSE_MULTIPLE_CONTEXT = PredefinedKey.valueOf("PARSE_MULTIPLE_CONTEXT", Boolean.class); /** * <p>Determines the number system. </p> * * <p>In case of changing the language setting this attribute will * automatically be adjusted. The {@link #ZERO_DIGIT attribute for zero digit} * will be adjusted, too (if the number system is decimal) but can be set to * another value however. </p> * * <p>If a non-decimal number system like roman numbers is chosen then Time4J * will ignore it for any other format component than ordinary integers. For * example, zone offsets in format ±hh:mm, fractional seconds, ordinals * like "1st" or two-digit-years cannot be printed in roman numbers. </p> * * @since 3.11/4.8 */ /*[deutsch] * <p>Bestimmt das Zahlsystem. </p> * * <p>Diese Einstellung wird bei jeder Änderung der Spracheinstellung * automatisch angepasst. Das {@link #ZERO_DIGIT Attribut für die Nullziffer} * wird auch angepasst (falls das Zahlsystem dezimal ist), kann aber abweichend * konfiguriert werden. </p> * * <p>Wenn ein nicht-dezimales Zahlsystem wie die römischen Zahlen gewählt wird, * dann wird Time4J dieses Attribut ignorieren, sobald eine Formatkomponente betroffen ist, * die nicht auf gewöhnlichen Integertypen basiert. Zum Beispiel können * Zeitzonen-Offsets im Format ±hh:mm, Sekundenbruchteile, Ordinalzahlen wie * "1st" (englisch) oder zweistellige Jahresangaben nicht in römischen * Zahlen dargestellt werden. </p> * * @since 3.11/4.8 */ public static final AttributeKey<NumberSystem> NUMBER_SYSTEM = PredefinedKey.valueOf("NUMBER_SYSTEM", NumberSystem.class); /** * <p>Determines the unicode char for the zero digit. </p> * * <p>In case of changing the language setting or the (decimal) number system this attribute * will automatically be adjusted to the default number system. For ISO-8601, the default * value is the arab digit {@code 0} (corresponding to the ASCII-value 48). </p> * * @see #NUMBER_SYSTEM */ /*[deutsch] * <p>Legt das Unicode-Zeichen für die Null-Ziffer fest. </p> * * <p>Diese Einstellung wird bei jeder Änderung der Spracheinstellung oder des * (Dezimal-)Zahlsystems automatisch an das jeweilige Standard-Zahlsystem angepasst. Standardwert * ist in ISO-8601 die arabische Ziffer {@code 0} (entsprechend dem ASCII-Wert 48). </p> * * @see #NUMBER_SYSTEM */ public static final AttributeKey<Character> ZERO_DIGIT = PredefinedKey.valueOf("ZERO_DIGIT", Character.class); /** * <p>This attribute controls if the formatter will stop using the localized * GMT prefix for representations of localized timezone offsets. </p> * * <p>Only relevant if the CLDR format pattern symbol O (or OOOO) is used. </p> * * @since 3.13/4.10 */ /*[deutsch] * <p>Steuert, ob der Formatierer das lokalisierte GMT-Präfix in der * Repräsentation von lokalisierten Zeitzonen-Offsets unterdrückt. </p> * * <p>Nur relevant, wenn das CLDR Formatmustersymbol O (oder OOOO) verwendet wird. </p> * * @since 3.13/4.10 */ public static final AttributeKey<Boolean> NO_GMT_PREFIX = PredefinedKey.valueOf("NO_GMT_PREFIX", Boolean.class); /** * <p>Determines the unicode char for the decimal separator. </p> * * <p>In case of changing the language setting this attribute will automatically * be adjusted. In ISO-8601 (for the root locale), the comma is the default value * is the comma corresponding to the ASCII-value 44. With help of the boolean * system property "net.time4j.format.iso.decimal.dot", the dot can be * defined as alternative default value. </p> */ /*[deutsch] * <p>Legt das Unicode-Zeichen für das Dezimaltrennzeichen fest. </p> * * <p>Diese Einstellung wird bei jeder Änderung der Spracheinstellung * automatisch angepasst. Standardwert ist in ISO-8601 das Komma (also * der ASCII-Wert 44). Mit Hilfe der bool'schen System-Property * "net.time4j.format.iso.decimal.dot" kann auch der Punkt * als alternativer Standardwert definiert werden. </p> */ public static final AttributeKey<Character> DECIMAL_SEPARATOR = PredefinedKey.valueOf("DECIMAL_SEPARATOR", Character.class); /** * <p>Determines the pad char to be used if a formatted representation is * shorter than specified. </p> * * <p>Default value is the space. Numerical elements are not affected * by this attribute because they always use the zero digit as pad char. </p> */ /*[deutsch] * <p>Legt das Füllzeichen in Textelementen fest, das verwendet wird, * wenn eine formatierte Darstellung kürzer als mindestens angegeben * ist. </p> * * <p>Standardwert ist das Leerzeichen. Numerische Elemente sind hiervon * nicht berührt, da sie immer die Nullziffer als Füllzeichen * verwenden. </p> */ public static final AttributeKey<Character> PAD_CHAR = PredefinedKey.valueOf("PAD_CHAR", Character.class); /** * <p>Determines the pivot year for the representation of * two-digit-years. </p> * * <p>Default value is the year which is 20 years after the current * year. Example: If the pivot year has the value {@code 2034} then * a two-digit-year will be mapped to the range 1934-2033 such that * the last two digits are equal. This attribute must have at least * three digits an be positive else an exception will be thrown. </p> */ /*[deutsch] * <p>Legt das Kippjahr zur zweistelligen Darstellung von Jahreselementen * fest. </p> * * <p>Standardwert ist das Jahr, das 20 Jahre nach dem aktuellen Jahr * liegt. Hat zum Beispiel das Kippjahr den Wert {@code 2034}, dann * wird eine zweistellige Jahresangabe auf das Intervall 1934-2033 * so abgebildet, daß die letzten zwei Ziffern gleich sind. * Kippjahresangaben müssen mindestens 3-stellig und positiv sein, * sonst wird ein solcher Versuch mit einer Ausnahme quittiert. </p> */ public static final AttributeKey<Integer> PIVOT_YEAR = PredefinedKey.valueOf("PIVOT_YEAR", Integer.class); /** * <p>Controls if any trailing unparsed characters will be * tolerated or not. </p> * * <p>Example: </p> * * <pre> * ChronoFormatter formatter = * ChronoFormatter.setUp(PlainTime.class, Locale.US) * .addInteger(PlainTime.CLOCK_HOUR_OF_AMPM, 1, 2) * .addLiteral(' ') * .addText(PlainTime.AM_PM_OF_DAY) * .padPrevious(3) * .addFixedInteger(PlainTime.MINUTE_OF_HOUR, 2) * .build() * .with(Attributes.TRAILING_CHARACTERS, true); * System.out.println(formatter.parse("5 PM 45xyz")); * // Output: T17:45 * </pre> * * <p>Starting with version v3.25/4.21, this attribute will only matter if users don't explicitly * specify a {@code ParseLog} or a {@code ParsePosition}. Otherwise - with explicit parse log or * position - the parser will always tolerate trailing characters. </p> * * <p>Default value: {@code false} in strict or smart mode </p> */ /*[deutsch] * <p>Steuert, ob beim Parsen verbleibende Zeichen der Texteingabe * toleriert werden. </p> * * <p>Beispiel: </p> * * <pre> * ChronoFormatter formatter = * ChronoFormatter.setUp(PlainTime.class, Locale.US) * .addInteger(PlainTime.CLOCK_HOUR_OF_AMPM, 1, 2) * .addLiteral(' ') * .addText(PlainTime.AM_PM_OF_DAY) * .padPrevious(3) * .addFixedInteger(PlainTime.MINUTE_OF_HOUR, 2) * .build() * .with(Attributes.TRAILING_CHARACTERS, true); * System.out.println(formatter.parse("5 PM 45xyz")); * // Output: T17:45 * </pre> * * <p>Beginnend mit der Version v3.25/4.21 spielt dieses Formatattribut nur eine Rolle, wenn Anwender * kein {@code ParseLog} oder eine {@code ParsePosition} angeben. Sonst - mit extra Angaben zur Position - * wird der Interpretierer immer verbleibende Zeichen tolerieren. </p> * * <p>Standardwert: {@code false} wenn kein laxer Modus vorliegt </p> */ public static final AttributeKey<Boolean> TRAILING_CHARACTERS = PredefinedKey.valueOf("TRAILING_CHARACTERS", Boolean.class); /** * <p>Determines how many remaining chars in a given text are reserved * and cannot be consumed by the current format step. </p> * * <p>Default value is {@code 0}. This attribute can be used as sectional * attribute if an integer element is numerically processed. Such a * protected element will not consume any following chars and possibly * use the default value setting of the current formatter instead. </p> * * <p>Note: This attribute overrides any reserved area due to * <i>adjacent digit parsing</i>. </p> * * @since 2.0 */ /*[deutsch] * <p>Legt fest, wieviele der verbleibenden Zeichen in einem zu * interpretierenden Text reserviert und damit nicht vom aktuellen * Formatschritt konsumiert werden können. </p> * * <p>Standardwert ist {@code 0}. Dieses Attribut eignet sich als * sektionales Attribut, wenn ein Integer-Element numerisch * verarbeitet wird. So ein geschütztes Element wird keine * folgenden Zeichen konsumieren und eventuell den Standardwert * des aktuellen Formatierers verwenden. </p> * * <p>Hinweis: Dieses Attribut überlagert reservierte Ziffernbereiche, * die dem Modus <i>adjacent digit parsing</i> von nachgelagerten Elementen * mit fester Ziffernbreite zuzuschreiben sind. </p> * * @since 2.0 */ public static final AttributeKey<Integer> PROTECTED_CHARACTERS = PredefinedKey.valueOf("PROTECTED_CHARACTERS", Integer.class); /** * <p>Defines an attribute key which can be used in queries for the calendar variant. </p> * * @since 3.5/4.3 */ /*[deutsch] * <p>Definiert ein Formatattribut, das in Abfragen nach der Kalendervariante verwendet werden kann. </p> * * @since 3.5/4.3 */ public static final AttributeKey<String> CALENDAR_VARIANT = PredefinedKey.valueOf("CALENDAR_VARIANT", String.class); /** * <p>Defines an attribute key which can be used in queries for the start of day during formatting or parsing. </p> * * <p>The default value is {@link StartOfDay#MIDNIGHT}. </p> * * @since 3.5/4.3 */ /*[deutsch] * <p>Definiert ein Formatattribut, das in Abfragen nach dem Start eines Kalendertages beim Formatieren * und Parsen verwendet werden kann. </p> * * <p>Der Standardwert ist {@link StartOfDay#MIDNIGHT}. </p> * * @since 3.5/4.3 */ public static final AttributeKey<StartOfDay> START_OF_DAY = PredefinedKey.valueOf("START_OF_DAY", StartOfDay.class); /** * <p>This attribute controls if style-driven date patterns should always have four-digit-years or not. </p> * * <p>Default value: {@code false} </p> * * @since 3.26/4.22 */ /*[deutsch] * <p>Steuert, ob stil-basierte Datumsformatmuster immer vierstellige Jahreszahlen haben oder nicht. </p> * * <p>Standardwert: {@code false} </p> * * @since 3.26/4.22 */ public static final AttributeKey<Boolean> FOUR_DIGIT_YEAR = PredefinedKey.valueOf("FOUR_DIGIT_YEAR", Boolean.class); private static final Attributes EMPTY = new Attributes.Builder().build(); //~ Instanzvariablen -------------------------------------------------- private final Map<String, Object> attributes; //~ Konstruktoren ----------------------------------------------------- private Attributes(Map<String, Object> map) { super(); this.attributes = Collections.unmodifiableMap(new HashMap<>(map)); } //~ Methoden ---------------------------------------------------------- /** * <p>Represents an empty collection of format attributes. </p> * * @return empty attribute query */ /*[deutsch] * <p>Repräsentiert eine leere Menge von Formatattributen. </p> * * @return empty attribute query */ public static Attributes empty() { return EMPTY; } /** * <p>Creates a new attribute key. </p> * * @param <A> generic immutable type of attribute value * @param name name of attribute * @param type type of attribute * @return new attribute key * @since 3.13/4.10 */ /*[deutsch] * <p>Erzeugt einen neuen Attributschlüssel. </p> * * @param <A> generic immutable type of attribute value * @param name name of attribute * @param type type of attribute * @return new attribute key * @since 3.13/4.10 */ public static <A> AttributeKey<A> createKey( String name, Class<A> type ) { return PredefinedKey.valueOf(name, type); } @Override public boolean contains(AttributeKey<?> key) { return this.attributes.containsKey(key.name()); } @Override public <A> A get(AttributeKey<A> key) { Object obj = this.attributes.get(key.name()); if (obj == null) { throw new NoSuchElementException(key.name()); } else { return key.type().cast(obj); } } @Override public <A> A get( AttributeKey<A> key, A defaultValue ) { Object obj = this.attributes.get(key.name()); if (obj == null) { return defaultValue; } else { return key.type().cast(obj); } } /** * <p>Compares all internal format attributes. </p> */ /*[deutsch] * <p>Vergleicht auf Basis aller internen Formatattribute. </p> */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof Attributes) { Attributes that = (Attributes) obj; return this.attributes.equals(that.attributes); } else { return false; } } /*[deutsch] * <p>Berechnet den Hash-Code. </p> */ @Override public int hashCode() { return this.attributes.hashCode(); } /** * <p>Supports mainly debugging. </p> */ /*[deutsch] * <p>Dient vorwiegend der Debugging-Unterstützung. </p> */ @Override public String toString() { StringBuilder sb = new StringBuilder(this.attributes.size() * 32); sb.append(this.getClass().getName()); sb.append('['); sb.append(this.attributes); sb.append(']'); return sb.toString(); } //~ Innere Klassen ---------------------------------------------------- /** * <p>Builds a collection of format attributes. </p> * * @doctags.concurrency {mutable} */ /*[deutsch] * <p>Baut eine Menge von Formatattributen. </p> * * @doctags.concurrency {mutable} */ public static final class Builder { //~ Instanzvariablen ---------------------------------------------- private final Map<String, Object> attributes = new HashMap<>(); //~ Konstruktoren ------------------------------------------------- /** * <p>Default constructor. </p> */ /*[deutsch] * <p>Standard-Konstruktor. </p> */ public Builder() { super(); } /** * <p>Constructor for determining the calendar type. </p> * * @param chronology object with possible calendar type * @since 3.0 * @see #CALENDAR_TYPE */ /*[deutsch] * <p>Konstruktor zum Ableiten des Kalendertyps. </p> * * @param chronology object with possible calendar type * @since 3.0 * @see #CALENDAR_TYPE */ public Builder(Chronology<?> chronology) { super(); this.setInternal(CALENDAR_TYPE, CalendarText.extractCalendarType(chronology)); } //~ Methoden ------------------------------------------------------ /** * <p>Sets the language. </p> * * @param locale new language setting * @return this instance for method chaining * @see #LANGUAGE */ /*[deutsch] * <p>Setzt die Spracheinstellung. </p> * * @param locale new language setting * @return this instance for method chaining * @see #LANGUAGE */ public Builder setLanguage(Locale locale) { this.setInternal(LANGUAGE, locale); return this; } /** * <p>Sets the timezone reference. </p> * * @param tzid timezone id * @return this instance for method chaining * @see #TIMEZONE_ID */ /*[deutsch] * <p>Setzt die Zeitzonenreferenz. </p> * * @param tzid timezone id * @return this instance for method chaining * @see #TIMEZONE_ID */ public Builder setTimezone(TZID tzid) { this.setInternal(TIMEZONE_ID, tzid); return this; } /** * <p>Sets the timezone reference. </p> * * @param tzid timezone id * @return this instance for method chaining * @throws IllegalArgumentException if given timezone cannot be loaded * @see #TIMEZONE_ID * @since 1.1 */ /*[deutsch] * <p>Setzt die Zeitzonenreferenz. </p> * * @param tzid timezone id * @return this instance for method chaining * @throws IllegalArgumentException if given timezone cannot be loaded * @see #TIMEZONE_ID * @since 1.1 */ public Builder setTimezone(String tzid) { this.setTimezone(Timezone.of(tzid).getID()); return this; } /** * <p>Sets the system timezone reference. </p> * * @return this instance for method chaining * @see #TIMEZONE_ID * @see Timezone#ofSystem() */ /*[deutsch] * <p>Legt die Systemzeitzone als Zeitzonenreferenz fest. </p> * * @return this instance for method chaining * @see #TIMEZONE_ID * @see Timezone#ofSystem() */ public Builder setStdTimezone() { return this.setTimezone(Timezone.ofSystem().getID()); } /** * <p>Sets the calendar variant. </p> * * @param variant calendar variant * @return this instance for method chaining * @see #CALENDAR_VARIANT * @see net.time4j.engine.CalendarVariant * @since 3.5/4.3 */ /*[deutsch] * <p>Setzt die Kalendervariante. </p> * * @param variant calendar variant * @return this instance for method chaining * @see #CALENDAR_VARIANT * @see net.time4j.engine.CalendarVariant * @since 3.5/4.3 */ public Builder setCalendarVariant(String variant) { this.setInternal(CALENDAR_VARIANT, variant); return this; } /** * <p>Sets an attribute of {@code boolean}-type. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining */ /*[deutsch] * <p>Setzt ein Formatattribut vom {@code boolean}-Typ. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining */ public Builder set( AttributeKey<Boolean> key, boolean value ) { this.attributes.put(key.name(), Boolean.valueOf(value)); return this; } /** * <p>Sets an attribute of {@code int}-type. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining * @throws IllegalArgumentException if an invalid pivot year is given */ /*[deutsch] * <p>Setzt ein Formatattribut vom {@code int}-Typ. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining * @throws IllegalArgumentException if an invalid pivot year is given */ public Builder set( AttributeKey<Integer> key, int value ) { if ( (key == Attributes.PIVOT_YEAR) && (value < 100) ) { throw new IllegalArgumentException( "Pivot year in far past not supported: " + value); } this.attributes.put(key.name(), Integer.valueOf(value)); return this; } /** * <p>Sets an attribute of {@code char}-type. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining */ /*[deutsch] * <p>Setzt ein Formatattribut vom {@code char}-Typ. </p> * * @param key attribute key * @param value attribute value * @return this instance for method chaining */ public Builder set( AttributeKey<Character> key, char value ) { this.attributes.put(key.name(), Character.valueOf(value)); return this; } /** * <p>Sets an attribute of {@code enum}-type. </p> * * @param <A> generic type of attribute * @param key attribute key * @param value attribute value * @return this instance for method chaining */ /*[deutsch] * <p>Setzt ein Formatattribut vom {@code enum}-Typ. </p> * * @param <A> generic type of attribute * @param key attribute key * @param value attribute value * @return this instance for method chaining */ public <A extends Enum<A>> Builder set( AttributeKey<A> key, A value ) { if (value == null) { throw new NullPointerException("Missing attribute value."); } else if (!(value instanceof Enum)) { throw new ClassCastException( // Schutz gegen raw-type-Fehler "Enum expected, but found: " + value); } this.attributes.put(key.name(), value); if (key.equals(Attributes.LENIENCY)) { switch (Leniency.class.cast(value)) { case STRICT: this.set(Attributes.PARSE_CASE_INSENSITIVE, false); this.set(Attributes.PARSE_PARTIAL_COMPARE, false); this.set(Attributes.TRAILING_CHARACTERS, false); this.set(Attributes.PARSE_MULTIPLE_CONTEXT, false); break; case SMART: this.set(Attributes.PARSE_CASE_INSENSITIVE, true); this.set(Attributes.PARSE_PARTIAL_COMPARE, false); this.set(Attributes.TRAILING_CHARACTERS, false); this.set(Attributes.PARSE_MULTIPLE_CONTEXT, true); break; case LAX: this.set(Attributes.PARSE_CASE_INSENSITIVE, true); this.set(Attributes.PARSE_PARTIAL_COMPARE, true); this.set(Attributes.TRAILING_CHARACTERS, true); this.set(Attributes.PARSE_MULTIPLE_CONTEXT, true); break; default: throw new UnsupportedOperationException(value.name()); } } else if (key.equals(Attributes.NUMBER_SYSTEM)) { NumberSystem numsys = NumberSystem.class.cast(value); if (numsys.isDecimal()) { this.set(Attributes.ZERO_DIGIT, numsys.getDigits().charAt(0)); } } return this; } /** * <p>Accepts all given attributes. </p> * * <p>If an attribute already exists then it will * be overridden. </p> * * @param attributes format attributes * @return this instance for method chaining */ /*[deutsch] * <p>Übernimmt alle angegebenen Attribute. </p> * * <p>Existiert ein Formatattribut schon, wird es * überschrieben. </p> * * @param attributes format attributes * @return this instance for method chaining */ public Builder setAll(Attributes attributes) { this.attributes.putAll(attributes.attributes); return this; } /** * <p>Removes the specified attribute. </p> * * @param key attribute key to be removed * @return this instance for method chaining */ /*[deutsch] * <p>Entfernt das angegebene Attribut. </p> * * @param key attribute key to be removed * @return this instance for method chaining */ public Builder remove(AttributeKey<?> key) { this.attributes.remove(key.name()); return this; } /** * <p>Creates a new unmodifiable collection of format attributes. </p> * * @return new instance of {@code Attributes} */ /*[deutsch] * <p>Erzeugt eine neue unveränderliche Instanz der * Formatattribute. </p> * * @return new instance of {@code Attributes} */ public Attributes build() { return new Attributes(this.attributes); } private <A> void setInternal( AttributeKey<A> key, A value ) { if (value == null) { throw new NullPointerException("Missing attribute value."); } this.attributes.put(key.name(), value); } } }