net.time4j.engine.ChronoEntity Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild,
* -----------------------------------------------------------------------
* This file (ChronoEntity.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 net.time4j.tz.TZID;
import java.util.Set;
/**
* Represents a temporal object which associates partial temporal values
* with chronological elements and also allows some manipulations of these
* element values.
*
* A {@code ChronoEntity} is usually a {@code TimePoint}, where the
* (primary) element values determine the position on a time axis such
* that a time arithmetic is possible. Alternatively a {@code ChronoEntity}
* can also represent a partial information like the combination of month
* and day-of-month for a simple display of a birthday.
*
* Chronological elements are either statically registered such that
* a direct access is enabled, or there is an (external) rule which
* enables read- and write-access. If no element rule can be found a
* {@code RuleNotFoundException} will be thrown.
*
* @param generic type of self reference
* @author Meno Hochschild
* @doctags.spec All public implementations must be immutable.
*/
/*[deutsch]
* Repräsentiert ein Zeitwertobjekt, das einzelne Werte mit
* chronologischen Elementen assoziiert und einen Zugriff auf diese
* Werte erlaubt.
*
* Eine {@code ChronoEntity} ist gewöhnlich ein {@code TimePoint},
* bei dem die (primären) Elementwerte zusammen die Position auf einem
* Zeitstrahl festlegen, so daß auch eine Zeitarithmetik möglich
* ist. Alternativ kann eine {@code ChronoEntity} eine chronologische
* Teilinformation wie z.B. die Kombination aus Monat und Tag zur einfachen
* Darstellung eines Geburtstags repräsentieren.
*
* Chronologische Elemente sind entweder vorab registriert, so daß
* ein Zugriff direkt möglich ist, oder es gibt eine Regel, die den
* Lese- bzw. Schreibzugriff ermöglicht. Ist die Regel nicht vorhanden,
* wird eine {@code RuleNotFoundException} geworfen.
*
* @param generic type of self reference
* @author Meno Hochschild
* @doctags.spec All public implementations must be immutable.
*/
public abstract class ChronoEntity>
implements ChronoDisplay {
//~ Methoden ----------------------------------------------------------
@Override
public boolean contains(ChronoElement> element) {
return this.getChronology().isSupported(element);
}
@Override
public V get(ChronoElement element) {
return this.getRule(element).getValue(this.getContext());
}
@Override
public int getInt(ChronoElement element) {
IntElementRule intRule = this.getChronology().getIntegerRule(element);
try {
if (intRule == null) {
return this.get(element).intValue();
} else {
return intRule.getInt(this.getContext());
}
} catch (ChronoException ex) {
return Integer.MIN_VALUE;
}
}
@Override
public V getMinimum(ChronoElement element) {
return this.getRule(element).getMinimum(this.getContext());
}
@Override
public V getMaximum(ChronoElement element) {
return this.getRule(element).getMaximum(this.getContext());
}
/**
* Lets given query evaluate this entity.
*
* Is equivalent to {@code function.apply(this)}. The applied
* strategy pattern hereby enables the externalization of querying
* the interpretation and evaluation of this entity, consequently
* enabling user-defined queries with arbitrary result types R.
* Main difference to chronological elements is read-only access.
* Users have to consult the documentation of given query to decide
* if this method will yield {@code null} or throws an exception
* if the result is undefined or otherwise not obtainable.
*
* @param generic type of result of query
* @param function time query
* @return result of query or {@code null} if undefined
* @throws ChronoException if the given query is not executable
*/
/*[deutsch]
* Läßt die angegebene Abfrage diese Entität
* auswerten.
*
* Entspricht {@code function.apply(this)}. Hierüber wird der
* Vorgang der Zeitinterpretation externalisiert und ermöglicht
* so benutzerdefinierte Abfragen mit beliebigen Ergebnistypen. Anders
* als bei chronologischen Elementen ist hier nur ein Lesezugriff
* möglich. In der Dokumentation der jeweiligen {@code ChronoFunction}
* ist nachzuschauen, ob diese Methode im Fall undefinierter Ergebnisse
* {@code null} zurückgibt oder eine Ausnahme wirft.
*
* @param generic type of result of query
* @param function time query
* @return result of query or {@code null} if undefined
* @throws ChronoException if the given query is not executable
*/
public final R get(ChronoFunction super T, R> function) {
return function.apply(this.getContext());
}
/**
* Queries if this entity matches given condition.
*
* Is equivalent to {@code condition.test(this)}. This method
* will never throw an exception. This behaviour is in contrast
* to that of {@code ChronoFunction}.
*
* @param condition temporal condition
* @return {@code true} if the given condition is matched by this
* entity else {@code false}
* @see #get(ChronoFunction)
*/
/*[deutsch]
* Genügt diese Instanz der angegebenen Bedingung?
*
* Entspricht {@code condition.test(this)}. Diese Methode wirft
* anders als eine allgemeine {@code ChronoFunction} keine Ausnahme.
*
* @param condition temporal condition
* @return {@code true} if the given condition is matched by this
* entity else {@code false}
* @see #get(ChronoFunction)
*/
public boolean matches(ChronoCondition super T> condition) {
return condition.test(this.getContext());
}
/**
* Tests if the value for given chronological value is invalid.
*
* Notes: This method tests if given value to be in question can
* be set via the expression {@code with(element, value)}. A numerical
* overflow situation (causing an {@code ArithmeticException}) will
* usually not be checked.
*
* @param generic type of element value
* @param element element the given value shall be assigned to
* @param value candidate value to be validated (optional)
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, Object) with(ChronoElement, V)
*/
/*[deutsch]
* Ist der Wert zum angegebenen chronologischen Element gültig?
*
* Hinweise: Diese Methode testet, ob der fragliche Wert mittels des
* Ausdrucks {@code with(element, value)} überhaupt gesetzt werden
* kann. Eine numerische Überlaufsituation im Hinblick auf eine
* {@code ArithmeticException} wird in der Regel nicht geprüft.
*
* @param generic type of element value
* @param element element the given value shall be assigned to
* @param value candidate value to be validated (optional)
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, Object) with(ChronoElement, V)
*/
public boolean isValid(
ChronoElement element,
V value
) {
if (element == null) {
throw new NullPointerException("Missing chronological element.");
}
return (
this.contains(element)
&& this.getRule(element).isValid(this.getContext(), value)
);
}
/**
* Tests if the value for given chronological value is invalid.
*
* Notes: This method tests if given value to be in question can
* be set via the expression {@code with(element, value)}. A numerical
* overflow situation (causing an {@code ArithmeticException}) will
* usually not be checked.
*
* @param element element the given value shall be assigned to
* @param value candidate value to be validated
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, int)
*/
/*[deutsch]
* Ist der Wert zum angegebenen chronologischen Element gültig?
*
* Hinweis: Eine numerische Überlaufsituation im Hinblick auf eine
* {@code ArithmeticException} wird nicht geprüft.
*
* @param element element the given value shall be assigned to
* @param value candidate value to be validated
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, int)
*/
public boolean isValid(
ChronoElement element,
int value
) {
IntElementRule intRule = this.getChronology().getIntegerRule(element);
if (intRule != null) {
return intRule.isValid(this.getContext(), value);
}
return this.isValid(element, Integer.valueOf(value));
}
/**
* Tests if the value for given chronological value is invalid.
*
* Notes: This method tests if given value to be in question can
* be set via the expression {@code with(element, value)}. A numerical
* overflow situation (causing an {@code ArithmeticException}) will
* usually not be checked.
*
* @param element element the given value shall be assigned to
* @param value candidate value to be validated
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, long)
*/
/*[deutsch]
* Ist der Wert zum angegebenen chronologischen Element gültig?
*
* Hinweis: Eine numerische Überlaufsituation im Hinblick auf eine
* {@code ArithmeticException} wird nicht geprüft.
*
* @param element element the given value shall be assigned to
* @param value candidate value to be validated
* @return {@code true} if the method {@code with()} can be called
* without exception else {@code false}
* @see #with(ChronoElement, long)
*/
public boolean isValid(
ChronoElement element,
long value
) {
return this.isValid(element, Long.valueOf(value));
}
/**
* Creates a copy of this instance with the changed element value.
*
* A {@code null} value will almost ever be seen as invalid causing an
* {@code IllegalArgumentException}. Subclasses which permit {@code null}
* must explicitly document this feature.
*
* @param generic type of element value
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
/*[deutsch]
* Erstellt eine Kopie dieser Instanz mit dem geänderten
* Elementwert.
*
* Ein {@code null}-Wert wird fast immer als ungültig angesehen,
* also zu einer {@code IllegalArgumentException} führen. Subklassen,
* die {@code null} zulassen, müssen das explizit dokumentieren.
*
* @param generic type of element value
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
public T with(
ChronoElement element,
V value
) {
return this.getRule(element).withValue(
this.getContext(),
value,
element.isLenient()
);
}
/**
* Creates a copy of this instance with the changed element value.
*
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
/*[deutsch]
* Erstellt eine Kopie dieser Instanz mit dem geänderten
* Elementwert.
*
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
public T with(
ChronoElement element,
int value
) {
IntElementRule intRule = this.getChronology().getIntegerRule(element);
if (intRule != null) {
return intRule.withValue(this.getContext(), value, element.isLenient());
}
return this.with(element, Integer.valueOf(value));
}
/**
* Creates a copy of this instance with the changed element value.
*
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
/*[deutsch]
* Erstellt eine Kopie dieser Instanz mit dem geänderten
* Elementwert.
*
* @param element chronological element
* @param value new element value
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if the element is not registered and there
* is no element rule for setting the value
* @throws IllegalArgumentException if the value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #isValid(ChronoElement, Object) isValid(ChronoElement, V)
*/
public T with(
ChronoElement element,
long value
) {
return this.with(element, Long.valueOf(value));
}
/**
* Creates a copy of this instance which is adjusted by given
* {@code ChronoOperator} using a strategy pattern approach.
*
* Is equivalent to {@code operator.apply(this)}. Hereby a user-defined
* manipulation will be externalized and is semantically similar to the
* reading counterpart {@code ChronoFunction}.
*
* @param operator operator for adjusting the element values
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if no element rule exists for setting the values
* @throws IllegalArgumentException if any new value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #get(ChronoFunction)
*/
/*[deutsch]
* Liefert eine Kopie dieser Instanz zurück, die mit Hilfe
* eines {@code ChronoOperator}-Objekts angepasst wird.
*
* Entspricht {@code operator.apply(this)}. Hierüber wird eine
* benutzerdefinierte Manipulation externalisiert und ist semantisch
* ähnlich wie im lesenden Gegenstück {@code ChronoFunction}.
*
* @param operator operator for adjusting the element values
* @return changed copy of the same type, this instance remains unaffected
* @throws ChronoException if no element rule exists for setting the values
* @throws IllegalArgumentException if any new value is not valid
* @throws ArithmeticException in case of arithmetic overflow
* @see #get(ChronoFunction)
*/
public T with(ChronoOperator operator) {
return operator.apply(this.getContext());
}
/**
* Queries if this entity contains a timezone for display purposes.
*
* This implementation has no timezone by default and yields
* {@code false}. Subclasses with a timezone reference intended for
* formatted output will override the method in a suitable way.
*
* @return {@code true} if a timezone is available and can be achieved
* with {@link #getTimezone()} else {@code false}
*/
/*[deutsch]
* Ermittelt, ob eine Zeitzone für Anzeigezwecke vorhanden ist.
*
* Diese Implementierung kennt standardmäßig keine Zeitzone
* und liefert {@code false}. Subklassen mit einem Zeitzonenbezug gedacht
* für formatierte Darstellungen werden die Methode in geeigneter Weise
* überschreiben.
*
* @return {@code true} if a timezone is available and can be achieved
* with {@link #getTimezone()} else {@code false}
*/
@Override
public boolean hasTimezone() {
return false;
}
/**
* Returns the associated timezone ID for display purposes
* if available.
*
* This implementation throws a {@code ChronoException}
* by default. Subclasses with a timezone reference intended for
* formatted output will override the method in a suitable way.
*
* @return timezone identifier if available
* @throws ChronoException if the timezone is not available
* @see #hasTimezone()
*/
/*[deutsch]
* Liefert die assoziierte Zeitzonen-ID für Anzeigezwecke,
* wenn vorhanden.
*
* Diese Implementierung wirft standardmäßig eine
* {@code ChronoException}. Subklassen mit einem Zeitzonenbezug
* gedacht für formatierte Darstellungen werden die Methode
* in geeigneter Weise überschreiben.
*
* @return timezone identifier if available
* @throws ChronoException if the timezone is not available
* @see #hasTimezone()
*/
@Override
public TZID getTimezone() {
throw new ChronoException("Timezone not available: " + this);
}
/**
* Yields all registered elements of this instance.
*
* Note that some (external) elements can be supported but not registered.
*
* @return unmodifiable set of internally registered elements
* @since 3.13/4.10
*/
/*[deutsch]
* Liefert alle registrierten Elemente dieser Instanz.
*
* Hinweis: Einige (externe) Elemente können unterstützt werden, sind aber nicht
* registriert.
*
* @return unmodifiable set of internally registered elements
* @since 3.13/4.10
*/
public Set> getRegisteredElements() {
return this.getChronology().getRegisteredElements();
}
/**
* Returns the assigned chronology which contains all necessary
* chronological rules.
*
* Concrete subclasses must create in a static initializer a
* chronology by help of {@code Chronology.Builder}, keep it as static
* constant and make it available here. Using the procedure guarantees
* that a basic set of registered elements and rules will be installed.
*
* @return chronological system
* @throws UnsupportedOperationException if not available (subclasses
* must document this extreme rare case)
* @see Chronology.Builder
*/
/*[deutsch]
* Liefert die zugehörige Chronologie, die alle notwendigen
* chronologischen Regeln enthält.
*
* Konkrete Subklassen müssen in einem static initializer
* mit Hilfe von {@code Chronology.Builder} oder {@code TimeAxis.Builder}
* eine Chronologie bauen, in einer eigenen Konstanten halten und hier
* verfügbar machen. Über dieses Verfahren wird zugleich ein
* Basissatz von Elementen und chronologischen Regeln vorinstalliert.
*
* @return chronological system
* @throws UnsupportedOperationException if not available (subclasses
* must document this rare case)
* @see Chronology.Builder
*/
protected abstract Chronology getChronology();
/**
* Liefert den Selbstbezug.
*
* @return time context (usually this instance)
*/
protected T getContext() {
Chronology c = this.getChronology();
Class type = c.getChronoType();
if (type.isInstance(this)) {
return type.cast(this);
} else {
for (ChronoElement> element : c.getRegisteredElements()) {
if (type == element.getType()) {
return type.cast(this.get(element));
}
}
}
throw new IllegalStateException(
"Implementation error: Cannot find entity context.");
}
/**
* Determines the associated element rule.
*
* @param value type of element
* @param element chronological element to be evaluated
* @return element rule
* @throws RuleNotFoundException if a rule cannot be determined
*/
ElementRule getRule(ChronoElement element) {
return this.getChronology().getRule(element);
}
}