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

net.time4j.engine.TimeAxis Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 

A time axis is a dynamic view on a chronology where a system of * registered time units is used to define a time arithmetic for any * time points belonging to this time axis respective chronology.

* * @param generic type of time units * @param generic type of time context compatible to {@link TimePoint}) * @author Meno Hochschild */ /*[deutsch] *

Eine Zeitachse ist eine dynamische Sicht auf eine Chronologie, * in der mit Hilfe eines Systems von Zeiteinheiten eine Zeitarithmetik auf * beliebigen Zeitpunkten der Chronologie definiert wird.

* * @param generic type of time units * @param generic type of time context compatible to {@link TimePoint}) * @author Meno Hochschild */ public final class TimeAxis> extends Chronology implements TimeLine { //~ Instanzvariablen -------------------------------------------------- private final Class unitType; private final Map> unitRules; private final Map unitLengths; private final Map> convertibleUnits; private final Map, U> baseUnits; private final T min; private final T max; private final CalendarSystem calendarSystem; private final ChronoElement self; private final TimeLine timeline; //~ Konstruktoren ----------------------------------------------------- private TimeAxis( Class chronoType, Class unitType, ChronoMerger merger, Map, ElementRule> ruleMap, Map> unitRules, final Map unitLengths, Map> convertibleUnits, List extensions, Map, U> baseUnits, T min, T max, CalendarSystem calendarSystem, // optional TimeLine timeline, // optional boolean useEnumUnits ) { super(chronoType, merger, ruleMap, extensions); Map> uRules = unitRules; if (useEnumUnits) { uRules = new IdentityHashMap<>(unitRules.size()); uRules.putAll(unitRules); } this.unitType = unitType; this.unitRules = uRules; this.unitLengths = Collections.unmodifiableMap(unitLengths); this.convertibleUnits = Collections.unmodifiableMap(convertibleUnits); this.baseUnits = Collections.unmodifiableMap(baseUnits); this.min = min; this.max = max; this.calendarSystem = calendarSystem; this.self = new SelfElement<>(chronoType, min, max); if (timeline == null) { List registeredUnits = new ArrayList<>(unitRules.keySet()); Collections.sort( registeredUnits, (unit1, unit2) -> Double.compare( getLength(unitLengths, unit1), getLength(unitLengths, unit2))); U step = registeredUnits.get(0); this.timeline = new DefaultTimeLine<>(step, min, max); } else { this.timeline = timeline; } } //~ Methoden ---------------------------------------------------------- /** *

Returns the type of supported time units.

* * @return reified type of time unit */ /*[deutsch] *

Liefert den Zeiteinheitstyp.

* * @return reified type of time unit */ public Class getUnitType() { return this.unitType; } /** *

Returns all registered time units.

* * @return unmodifiable set of registered units without duplicates */ /*[deutsch] *

Liefert alle registrierten Zeiteinheiten.

* * @return unmodifiable set of registered units without duplicates */ public Set getRegisteredUnits() { return Collections.unmodifiableSet(this.unitRules.keySet()); } /** *

Queries if given time unit is registered.

* * @param unit time unit (optional) * @return {@code true} if registered else {@code false} */ /*[deutsch] *

Ist die angegebene Zeiteinheit registriert?

* * @param unit time unit (optional) * @return {@code true} if registered else {@code false} */ public boolean isRegistered(U unit) { return this.unitRules.containsKey(unit); } /** *

Queries if given time unit is supported.

* *

A time unit is supported if it is either registered or * if it defines a suitable rule.

* * @param unit time unit (optional) * @return {@code true} if supported else {@code false} * @see BasicUnit#derive(Chronology) */ /*[deutsch] *

Wird die angegebene Zeiteinheit unterstützt?

* *

Unterstützung ist gegeben, wenn die Einheit entweder registriert * ist oder eine zu dieser Chronologie passende Regel definiert.

* * @param unit time unit (optional) * @return {@code true} if supported else {@code false} * @see BasicUnit#derive(Chronology) */ public boolean isSupported(U unit) { if (this.isRegistered(unit)) { return true; } else if (unit instanceof BasicUnit) { return (BasicUnit.class.cast(unit).derive(this) != null); } else { return false; } } /** *

Returns the length of given time unit in seconds as it is * usual or estimated on this time axis.

* *

Example: In ISO-systems the year has {@code 365.2425 * 86400} * seconds by default (mean average), in a julian calender * {@code 365.25 * 86400} seconds however. Daylight-saving-transitions * or UTC-leapseconds are not counted here.

* *

Note: If given time unit is not registered then Time4J tries * to interprete the unit as {@code ChronoUnit}. If this fails, too, * then the length is not calculatable.

* * @param unit time unit * @return estimated standard length in seconds or * {@code Double.NaN} if not calculatable * @see ChronoUnit */ /*[deutsch] *

Liefert die in dieser Chronologie übliche Länge der * angegebenen Zeiteinheit in Sekunden.

* *

Beispiel: In ISO-Systemen hat das Jahr standardmäßig * {@code 365.2425 * 86400} Sekunden, in einem julianischen Kalender * hingegen {@code 365.25 * 86400} Sekunden. DST-Übergänge * in Zeitzonen oder UTC-Schaltsekunden werden nicht mitgezählt.

* *

Hinweis: Ist die angegebene Zeiteinheit nicht registriert, wird * versucht, die Zeiteinheit als {@code ChronoUnit} zu interpretieren. * Schlägt auch das fehl, ist die Länge nicht ermittelbar.

* * @param unit time unit * @return estimated standard length in seconds or * {@code Double.NaN} if not calculatable * @see ChronoUnit */ public double getLength(U unit) { return getLength(this.unitLengths, unit); } /** *

Queries if given time units are convertible.

* *

Convertibility means that there exists a fixed integer factor * for conversion between the units. Examples for convertible units * are weeks/days (factor 7) or years/months (factor 12) in ISO-systems. * Otherwise minutes and seconds will only be convertible with factor * {@code 60} if there is no UTC-context with possible leap seconds.

* *

If two time units are convertible then the length of a time unit * ({@code getLength()}) can be used to convert time units by applying * the rounded quotient of lengths of units.

* * @param unit1 first time unit * @param unit2 second time unit * @return {@code true} if convertible else {@code false} * @see #getLength(Object) getLength(U) */ /*[deutsch] *

Sind die angegebenen Zeiteinheiten ineinander konvertierbar?

* *

Konvertierbarkeit bedeutet, daß immer ein konstanter * ganzzahliger Faktor zur Umrechnung zwischen den Zeiteinheiten * angewandt werden kann. Beispiele für konvertierbare Einheiten * sind in ISO-basierten Kalendersystemen die Paare Wochen/Tage * (Faktor 7) oder Jahre/Monate (Faktor 12). Andererseits sind Minuten * und Sekunden zueinander nur dann konvertierbar mit dem Faktor * {@code 60}, wenn kein UTC-Kontext mit möglichen Schaltsekunden * vorliegt.

* *

Ist die Konvertierbarkeit gegeben, darf die Länge einer * Zeiteinheit mittels der Methode {@code getLength()} herangezogen * werden, um Zeiteinheiten umzurechnen, indem als Faktor der gerundete * Quotient der Längen der Zeiteinheiten bestimmt wird.

* * @param unit1 first time unit * @param unit2 second time unit * @return {@code true} if convertible else {@code false} * @see #getLength(Object) getLength(U) */ public boolean isConvertible( U unit1, U unit2 ) { Set set = this.convertibleUnits.get(unit1); return ((set != null) && set.contains(unit2)); } /** *

Queries if given element has a base unit.

* * @param element chronological element (optional) * @return {@code true} if given element has a base unit else {@code false} * @see #getBaseUnit(ChronoElement) */ /*[deutsch] *

Ermittelt, ob das angegebene Element eine Basiseinheit hat.

* * @param element chronological element (optional) * @return {@code true} if given element has a base unit else {@code false} * @see #getBaseUnit(ChronoElement) */ public boolean hasBaseUnit(ChronoElement element) { if (element == null) { return false; } boolean found = this.baseUnits.containsKey(element); if ( !found && (element instanceof BasicElement) ) { ChronoElement parent = ((BasicElement) element).getParent(); found = ((parent != null) && this.baseUnits.containsKey(parent)); } return found; } /** *

Returns the base unit of given element if available.

* *

Only registred elements can have a base unit unless the element * is a {@code BasicElement} and refers another registered element with * a base unit.

* * @param element chronological element * @return found base unit * @throws ChronoException if there is no base unit * @see #hasBaseUnit(ChronoElement) * @see BasicElement#getParent() */ /*[deutsch] *

Liefert die Basiseinheit zum angegebenen Element.

* *

Nur registrierte Elemente können eine Basiseinheit haben, es * sei denn, das angegebene Element ist ein {@code BasicElement} und * referenziert ein anderes auf dieser Zeitachse registriertes Element * mit einer Basiseinheit.

* * @param element chronological element * @return found base unit * @throws ChronoException if there is no base unit * @see #hasBaseUnit(ChronoElement) * @see BasicElement#getParent() */ public U getBaseUnit(ChronoElement element) { if (element == null) { throw new NullPointerException("Missing element."); } U baseUnit = this.baseUnits.get(element); if ( (baseUnit == null) && (element instanceof BasicElement) ) { ChronoElement parent = ((BasicElement) element).getParent(); baseUnit = this.baseUnits.get(parent); } if (baseUnit == null) { throw new ChronoException( "Base unit not found for: " + element.name()); } return baseUnit; } /** *

Compares time units by ascending precision (that is descending length).

* *

Note: Before release v4.21, the time axis implemented {@code Comparator}, not {@code Comparator}. * This new method serves as the replacement for the old comparator method.

* * @return Comparator * @see #compare(TimePoint, TimePoint) * @since 3.25/4.21 */ /*[deutsch] *

Vergleicht Zeiteinheiten nach wachsender Genauigkeit (also abnehmender Länge).

* *

Hinweis: Vor dem Release v4.21 hat diese Klasse das Interface {@code Comparator} und * nicht {@code Comparator} implementiert. Diese neue Methode dient als Ersatz für die * alte Vergleichsmethode.

* * @return Comparator * @see #compare(TimePoint, TimePoint) * @since 3.25/4.21 */ public Comparator unitComparator() { return (unit1, unit2) -> Double.compare(this.getLength(unit2), this.getLength(unit1)); } /** *

Compares points in time by their temporal position on the timeline.

* * @param first the first point in comparison * @param second the second point in comparison * @return positive, zero or negative number if {@code first} is later, simultaneous or earlier than {@code second} * @since 3.25/4.21 */ /*[deutsch] *

Vergleicht Zeitpunkte nach ihrer temporalen Position auf dem Zeitstrahl.

* * @param first the first point in comparison * @param second the second point in comparison * @return positive, zero or negative number if {@code first} is later, simultaneous or earlier than {@code second} * @since 3.25/4.21 */ @Override public int compare( T first, T second ) { return first.compareTo(second); } /** *

Yields the minimum of this time axis.

* * @return earliest possible time point */ /*[deutsch] *

Ermittelt das Minimum auf der Zeitachse.

* * @return earliest possible time point */ public T getMinimum() { return this.min; } /** *

Yields the maximum of this time axis.

* * @return latest possible time point */ /*[deutsch] *

Ermittelt das Maximum auf der Zeitachse.

* * @return latest possible time point */ public T getMaximum() { return this.max; } @Override public boolean hasCalendarSystem() { return (this.calendarSystem != null); } @Override public CalendarSystem getCalendarSystem() { if (this.calendarSystem == null) { return super.getCalendarSystem(); } else { return this.calendarSystem; } } @Override public CalendarSystem getCalendarSystem(String variant) { if (variant.isEmpty()) { return this.getCalendarSystem(); } return super.getCalendarSystem(variant); } @Override @Deprecated public T createFrom( ChronoEntity entity, AttributeQuery attributes, boolean preparsing ) { if (entity.contains(this.self)) { return entity.get(this.self); } return super.createFrom(entity, attributes, preparsing); } @Override public T createFrom( ChronoEntity entity, AttributeQuery attributes, boolean lenient, boolean preparsing ) { if (entity.contains(this.self)) { return entity.get(this.self); } return super.createFrom(entity, attributes, lenient, preparsing); } /** *

Yields this time axis as chronological self-referencing * element.

* * @return self-referencing element */ /*[deutsch] *

Liefert diese Zeitachse als chronologisches Element mit * Selbstbezug.

* * @return self-referencing element */ public ChronoElement element() { return this.self; } @Override public T stepForward(T timepoint) { return this.timeline.stepForward(timepoint); } @Override public T stepBackwards(T timepoint) { return this.timeline.stepBackwards(timepoint); } /** *

Liefert die chronologische Regel zur angegebenen Zeiteinheit.

* * @param unit time unit * @return unit rule or {@code null} if not registered */ UnitRule getRule(U unit) { if (unit == null) { throw new NullPointerException("Missing chronological unit."); } UnitRule rule = this.unitRules.get(unit); if (rule == null) { if (unit instanceof BasicUnit) { rule = BasicUnit.class.cast(unit).derive(this); } if (rule == null) { throw new RuleNotFoundException(this, unit); } } return rule; } private static double getLength( Map unitLengths, U unit ) { Double length = unitLengths.get(unit); if (length == null) { if (unit instanceof ChronoUnit) { return ChronoUnit.class.cast(unit).getLength(); } else { return Double.NaN; } } else { return length.doubleValue(); } } //~ Innere Klassen ---------------------------------------------------- /** *

Creates a builder for a new time axis respective chronology * and will only be used during loading a class of type * {@code TimePoint (T)} in a static initializer.

* *

Instances of this class will be created by the static factory * methods {@code setUp()}.

* * @param generic type of time unit * @param generic type of time context * @author Meno Hochschild * @see #setUp(Class,Class,ChronoMerger,TimePoint,TimePoint) * @see #setUp(Class,Class,ChronoMerger,CalendarSystem) * @doctags.concurrency {mutable} */ /*[deutsch] *

Erzeugt einen Builder für eine neue Zeitachse respektive * Chronologie und wird ausschließlich beim Laden einer * {@code TimePoint}-Klasse T in einem static initializer * benutzt.

* *

Instanzen dieser Klasse werden über die statischen * {@code setUp()}-Fabrikmethoden erzeugt.

* * @param generic type of time unit * @param generic type of time context * @author Meno Hochschild * @see #setUp(Class,Class,ChronoMerger,TimePoint,TimePoint) * @see #setUp(Class,Class,ChronoMerger,CalendarSystem) * @doctags.concurrency {mutable} */ public static final class Builder> extends Chronology.Builder { //~ Instanzvariablen ---------------------------------------------- private final Class unitType; private final Map> unitRules; private final Map unitLengths; private final Map> convertibleUnits; private final Map, U> baseUnits; private final T min; private final T max; private final CalendarSystem calendarSystem; private TimeLine timeline = null; private boolean useEnumUnits = true; //~ Konstruktoren ------------------------------------------------- private Builder( Class unitType, Class chronoType, ChronoMerger merger, T min, T max, CalendarSystem calendarSystem, // optional TimeLine timeline ) { super(chronoType, merger); if (unitType == null) { throw new NullPointerException("Missing unit type."); } else if (min == null) { throw new NullPointerException("Missing minimum of range."); } else if (max == null) { throw new NullPointerException("Missing maximum of range."); } else if ( Calendrical.class.isAssignableFrom(chronoType) && (calendarSystem == null) ) { throw new NullPointerException("Missing calendar system."); } this.unitType = unitType; this.unitRules = new HashMap<>(); this.unitLengths = new HashMap<>(); this.convertibleUnits = new HashMap<>(); this.baseUnits = new HashMap<>(); this.min = min; this.max = max; this.calendarSystem = calendarSystem; this.timeline = timeline; } //~ Methoden ------------------------------------------------------ /** *

Creates a builder for building a chronological but * non-calendrical system.

* * @param generic type of time unit * @param generic type of time context * @param unitType reified type of time units * @param chronoType reified chronological type * @param merger generic replacement for static * creation of time points * @param min minimum value on time axis * @param max maximum value on time axis * @return new {@code Builder} object */ /*[deutsch] *

Erzeugt ein Hilfsobjekt zum Bauen eines chronologischen, aber * nicht kalendarischen Systems.

* * @param generic type of time unit * @param generic type of time context * @param unitType reified type of time units * @param chronoType reified chronological type * @param merger generic replacement for static * creation of time points * @param min minimum value on time axis * @param max maximum value on time axis * @return new {@code Builder} object */ public static > Builder setUp( Class unitType, Class chronoType, ChronoMerger merger, T min, T max ) { return new Builder<>( unitType, chronoType, merger, min, max, null, null); } /** *

Creates a builder for building a time axis for * plain calendrical objects.

* * @param generic type of time unit * @param generic type of date context * @param unitType reified type of time units * @param chronoType reified chronological type * @param merger generic replacement for static * creation of time points * @param calendarSystem calender system * @return new {@code Builder} object */ /*[deutsch] *

Erzeugt ein Hilfsobjekt zum Bauen einer Zeitachse für * reine Datumsangaben.

* * @param generic type of time unit * @param generic type of date context * @param unitType reified type of time units * @param chronoType reified chronological type * @param merger generic replacement for static * creation of time points * @param calendarSystem calender system * @return new {@code Builder} object */ public static > Builder setUp( Class unitType, Class chronoType, ChronoMerger merger, CalendarSystem calendarSystem ) { final CalendarSystem calsys = calendarSystem; Builder builder = new Builder<>( unitType, chronoType, merger, calsys.transform(calsys.getMinimumSinceUTC()), calsys.transform(calsys.getMaximumSinceUTC()), calsys, null ); for (EpochDays element : EpochDays.values()) { builder.appendElement(element, element.derive(calsys)); } return builder; } @Override public Builder appendElement( ChronoElement element, ElementRule rule ) { super.appendElement(element, rule); return this; } /** *

Registers a new element with associated rule and a base * unit.

* * @param generic type of element values * @param element chronological element to be registered * @param rule associated element rule * @param baseUnit base unit for rolling operations * @return this instance for method chaining * @throws IllegalArgumentException if given element is already * registered (duplicate) */ /*[deutsch] *

Registriert ein neues Element mitsamt der assoziierten Regel * und einer Basiseinheit.

* * @param generic type of element values * @param element chronological element to be registered * @param rule associated element rule * @param baseUnit base unit for rolling operations * @return this instance for method chaining * @throws IllegalArgumentException if given element is already * registered (duplicate) */ public Builder appendElement( ChronoElement element, ElementRule rule, U baseUnit ) { if (baseUnit == null) { throw new NullPointerException("Missing base unit."); } super.appendElement(element, rule); this.baseUnits.put(element, baseUnit); return this; } /** *

Registers a non-convertible time unit with an associated * unit rule.

* *

Is equivalent to {@link #appendUnit(Object,UnitRule,double,Set) * appendUnit(U, rule, length, Collections.emptySet())}.

* * @param unit time unit to be registered * @param rule associated unit rule * @param length estimated standard length in seconds * @return this instance for method chaining * @throws IllegalArgumentException if given time unit is already * registered (duplicate) or if given length does not represent * any decimal number */ /*[deutsch] *

Registriert eine neue nicht-konvertierbare Zeiteinheit mitsamt * Einheitsregel.

* *

Entspricht {@link #appendUnit(Object,UnitRule,double,Set) * appendUnit(U, rule, length, Collections.emptySet())}.

* * @param unit time unit to be registered * @param rule associated unit rule * @param length estimated standard length in seconds * @return this instance for method chaining * @throws IllegalArgumentException if given time unit is already * registered (duplicate) or if given length does not represent * any decimal number */ public Builder appendUnit( U unit, UnitRule rule, double length ) { Set none = Collections.emptySet(); return this.appendUnit(unit, rule, length, none); } /** *

Registers a new time unit with an associated unit rule.

* *

The unit rule defines the time arithmetic for addition and * subtraction of given unit suitable for the time axis.

* *

If the unit has a length of a whole day then the given length * must be equal to {@code 86400.0}. The time unit of a second has * always the length {@code 1.0}.

* *

The default length of a time unit primarily serves for * conversion purposes. Rare anomalies like leap seconds or * timezone-induced jumps are not taken into account. Therefore * this estimated length is usually not equal to the real length * in a given time context. In case of non-convertible units the * estimated length has only informational meaning. Example: Months * have as length in ISO-systems the length of a gregorian year * in seconds divided by {@code 12} while in coptic calendar the * divisor {@code 13} is used. However, the definition of the length * of a month in days is not suitable because months and days are * not convertible.

* *

Convertibility exists if a time unit con be converted to * another time unit using a fixed integer factor. If this is not * always the case then an empty {@code Set} is to be used. Example * minutes/seconds: Without an UTC-context (with possible leapseonds) * minutes are convertible to seconds using a constant factor of * {@code 60}. In an UTC-context however, there is no convertibility * and hence the second will be missing in the argument. * {@code convertibleUnits} to minutes as unit to be registered * (and reverse, too)..

* * @param unit time unit to be registered * @param rule associated unit rule * @param length estimated standard length in seconds * @param convertibleUnits other time units which {@code unit} * can be converted to * @return this instance for method chaining * @throws IllegalArgumentException if given time unit is already * registered (duplicate) or if given length does not represent * any decimal number */ /*[deutsch] *

Registriert eine neue Zeiteinheit mitsamt Einheitsregel.

* *

Die Regel zur Zeiteinheit definiert die Zeitarithmetik in der * Addition und Subtraktion passend zur aktuellen Chronologie.

* *

Liegt eine Zeiteinheit mit Tageslänge vor, so ist stets * die Länge von {@code 86400.0} anzugeben. Die Zeiteinheit der * Sekunde selbst hat die Länge {@code 1.0}.

* *

Die Standardlänge einer Zeiteinheit dient in erster Linie * der Konversion von zwei nach dem Kriterium der Genauigkeit * benachbarten Zeiteinheiten. Seltene Anomalien wie Schaltsekunden * oder zeitzonenbedingte Lücken werden dabei nicht kalkuliert. * Deshalb ist die Standardlänge nicht mit der im gegebenen * Zeitkontext gegebenen realen Länge einer Zeiteinheit * gleichzusetzen. Sind außerdem Zeiteinheiten nicht * konvertierbar, so hat auch die Standardlänge nur eine * rein informelle Bedeutung. Beispiel: Monate haben in ISO-Systemen * die Länge eines Jahres in Sekunden dividiert durch {@code 12}, * während im koptischen Kalender konstant der Dividend {@code 13} * beträgt. Hingegen ist eine Definition der Länge eines * Monats in Form von x Tagen ungeeignet, weil Monate und Tage * zueinander nicht konvertierbar sind.

* *

Als konvertierbar gilt die Zeiteinheit genau dann, wenn sie sich * mit einem festen ganzzahligen Faktor in eine andere Zeiteinheit * umrechnen lässt. Ist das nicht der Fall, dann ist ein leeres * {@code Set} anzugeben. Beispiel Minuten/Sekunden: Ohne einen * vorhandenen UTC-Kontext (mit möglichen Schaltsekunden) sind * Minuten zu konstant 60 Sekunden konvertierbar. In einem UTC-Kontext * darf jedoch die Sekundeneinheit nicht als konvertierbar angegeben * werden und fehlt deshalb im Argument {@code convertibleUnits} zu * Minuten als zu registrierender Zeiteinheit (und umgekehrt).

* * @param unit time unit to be registered * @param rule associated unit rule * @param length estimated standard length in seconds * @param convertibleUnits other time units which {@code unit} * can be converted to * @return this instance for method chaining * @throws IllegalArgumentException if given time unit is already * registered (duplicate) or if given length does not represent * any decimal number */ public Builder appendUnit( U unit, UnitRule rule, double length, Set convertibleUnits ) { if (unit == null) { throw new NullPointerException("Missing time unit."); } else if (rule == null) { throw new NullPointerException("Missing unit rule."); } this.checkUnitDuplicates(unit); if (convertibleUnits.contains(null)) { throw new NullPointerException( "Found convertible unit which is null."); } if (Double.isNaN(length)) { throw new IllegalArgumentException("Not a number: " + length); } else if (Double.isInfinite(length)) { throw new IllegalArgumentException("Infinite: " + length); } this.useEnumUnits = this.useEnumUnits && (unit instanceof Enum); this.unitRules.put(unit, rule); this.unitLengths.put(unit, length); Set set = new HashSet<>(convertibleUnits); set.remove(unit); // Selbstbezug entfernen this.convertibleUnits.put(unit, set); return this; } @Override public Builder appendExtension(ChronoExtension extension) { super.appendExtension(extension); return this; } /** *

Defines the argument as timeline to be used for stepping * forward or backwards.

* * @param timeline time line to be used * @return this instance for method chaining * @since 2.0 */ /*[deutsch] *

Definiert das Argument als Zeitstrahl, auf dem schrittweise * vorwärts oder rückwärts gegangen wird.

* * @param timeline time line to be used * @return this instance for method chaining * @since 2.0 */ public Builder withTimeLine(TimeLine timeline) { if (timeline == null) { throw new NullPointerException("Missing time line."); } this.timeline = timeline; return this; } /** *

Creates and registers a time axis.

* * @return new chronology as time axis * @throws IllegalStateException if already registered or in * case of inconsistencies */ /*[deutsch] *

Erzeugt und registriert eine Zeitachse.

* * @return new chronology as time axis * @throws IllegalStateException if already registered or in * case of inconsistencies */ @Override public TimeAxis build() { if (this.unitRules.isEmpty()) { throw new IllegalStateException("No time unit was registered."); } TimeAxis engine = new TimeAxis<>( this.chronoType, this.unitType, this.merger, this.ruleMap, this.unitRules, this.unitLengths, this.convertibleUnits, this.extensions, this.baseUnits, this.min, this.max, this.calendarSystem, this.timeline, this.useEnumUnits ); Chronology.register(engine); return engine; } private void checkUnitDuplicates(U unit) { if (this.time4j) { return; } // Instanzprüfung for (U key : this.unitRules.keySet()) { if (key.equals(unit)) { throw new IllegalArgumentException( "Unit duplicate found: " + unit.toString()); } } // Namensprüfung if (unit instanceof Enum) { String name = Enum.class.cast(unit).name(); for (U key : this.unitRules.keySet()) { if ( (key instanceof Enum) && Enum.class.cast(key).name().equals(name) ) { throw new IllegalArgumentException( "Unit duplicate found: " + name); } } } } } private static class SelfElement> extends BasicElement implements ElementRule { //~ Statische Felder/Initialisierungen ---------------------------- private static final long serialVersionUID = 4777240530511579802L; //~ Instanzvariablen ---------------------------------------------- private final Class type; private final T min; private final T max; //~ Konstruktoren ------------------------------------------------- private SelfElement( Class type, T min, T max ) { super(type.getName() + "-AXIS"); this.type = type; this.min = min; this.max = max; } //~ Methoden ------------------------------------------------------ @Override public Class getType() { return this.type; } @Override public T getDefaultMinimum() { return this.min; } @Override public T getDefaultMaximum() { return this.max; } @Override public boolean isDateElement() { return false; } @Override public boolean isTimeElement() { return false; } @Override public T getValue(T context) { return context; } @Override public T getMinimum(T context) { return this.getDefaultMinimum(); } @Override public T getMaximum(T context) { return this.getDefaultMaximum(); } @Override public boolean isValid( T context, T value ) { return (value != null); } @Override public T withValue( T context, T value, boolean lenient ) { if (value == null) { throw new IllegalArgumentException("Missing value."); } return value; } @Override public ChronoElement getChildAtFloor(T context) { throw new UnsupportedOperationException(); } @Override public ChronoElement getChildAtCeiling(T context) { throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unchecked") protected > ElementRule derive(Chronology chronology) { if (chronology.getChronoType().equals(this.type)) { return (ElementRule) this; } return null; } @Override protected boolean isSingleton() { return true; // used only once per chronology } @Override protected String getVeto(Chronology chronology) { return null; } } private static class DefaultTimeLine> implements TimeLine { //~ Instanzvariablen ---------------------------------------------- private final U step; private final T min; private final T max; //~ Konstruktoren ------------------------------------------------- DefaultTimeLine( U step, T min, T max ) { super(); this.step = step; this.min = min; this.max = max; } //~ Methoden ------------------------------------------------------ @Override public T stepForward(T timepoint) { if (timepoint.compareTo(this.max) >= 0) { return null; } return timepoint.plus(1, this.step); } @Override public T stepBackwards(T timepoint) { if (timepoint.compareTo(this.min) <= 0) { return null; } return timepoint.minus(1, this.step); } @Override public T getMinimum() { return this.min; } @Override public T getMaximum() { return this.max; } @Override public int compare(T t1, T t2) { return t1.compareTo(t2); } } }