/* * ----------------------------------------------------------------------- * Copyright © 2013-2015 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (TimeSpan.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.engine; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.List; /** * <p>Represents a common time span with an associated sign and * a sequence of time units and related amounts. </p> * * @param <U> generic type of time unit * @author Meno Hochschild */ /*[deutsch] * <p>Repräsentiert eine allgemeine vorzeichenbehaftete Zeitspannem * in mehreren Zeiteinheiten mit deren zugeordneten Beträgen. </p> * * @param <U> generic type of time unit * @author Meno Hochschild */ public interface TimeSpan<U> { //~ Methoden ---------------------------------------------------------- /** * <p>Yields all containted time span items with amount and unit in * the order from largest to smallest time units. </p> * * @return unmodifiable list sorted by precision of units in ascending * order where every time unit exists at most once */ /*[deutsch] * <p>Liefert alle enthaltenen Zeitspannenelemente mit Einheit und Betrag * in der Reihenfolge von den größten zu den kleinsten und * genauesten Zeiteinheiten. </p> * * @return unmodifiable list sorted by precision of units in ascending * order where every time unit exists at most once */ List<Item<U>> getTotalLength(); /** * <p>Queries if given time unit is part of this time span. </p> * * <p>By default the implementation uses following expression: </p> * * <pre> * for (Item<?> item : getTotalLength()) { * if (item.getUnit().equals(unit)) { * return (item.getAmount() > 0); * } * } * return false; * </pre> * * @param unit time unit to be asked (optional) * @return {@code true} if exists else {@code false} * @see #getPartialAmount(Object) getPartialAmount(U) */ /*[deutsch] * <p>Ist die angegebene Zeiteinheit in dieser Zeitspanne enthalten? </p> * * <p>Standardmäßig entspricht die konkrete * Implementierung folgendem Ausdruck: </p> * * <pre> * for (Item<?> item : getTotalLength()) { * if (item.getUnit().equals(unit)) { * return (item.getAmount() > 0); * } * } * return false; * </pre> * * @param unit time unit to be asked (optional) * @return {@code true} if exists else {@code false} * @see #getPartialAmount(Object) getPartialAmount(U) */ boolean contains(U unit); /** * <p>Yields the partial amount associated with given time unit. </p> * * <p>The method returns {@code 0} if this time span does not contain * given time unit. In order to get the total length/amount of this * time span users have to evaluate the method {@link #getTotalLength()} * instead. </p> * * @param unit time unit (optional) * @return amount as part of time span ({@code >= 0}) */ /*[deutsch] * <p>Liefert den Teilbetrag zur angegebenen Einheit als Absolutwert. </p> * * <p>Die Methode liefert {@code 0}, wenn die Zeiteinheit nicht enthalten * ist. Um den Gesamtwert der Zeitspanne zu bekommen, ist in der Regel * nicht diese Methode, sondern {@link #getTotalLength()} auszuwerten. </p> * * @param unit time unit (optional) * @return amount as part of time span ({@code >= 0}) */ long getPartialAmount(U unit); /** * <p>Queries if this time span is negative. </p> * * <p>A negative time span relates to the subtraction of two time * points where first one is after second one. The partial amounts * of every time span are never negative. Hence this attribute is not * associated with the partial amounts but only with the time span * itself. </p> * * <p>Note: An empty time span itself is never negative in agreement * with the mathematical relation {@code (-1) * 0 = 0}. </p> * * @return {@code true} if negative and not empty else {@code false} */ /*[deutsch] * <p>Ist diese Zeitspanne negativ? </p> * * <p>Der Begriff der negativen Zeitspanne bezieht sich auf die Subtraktion * zweier Zeitpunkte, von denen der erste vor dem zweiten liegt. Die * einzelnen Beträge der Zeitspanne sind nie negativ. Dieses Attribut * ist daher nicht mit den einzelnen Beträgen assoziiert, sondern * bezieht sich nur auf die Zeitspanne insgesamt. </p> * * <p>Notiz: Eine leere Zeitspanne ist selbst niemals negativ in * Übereinstimmung mit der mathematischen Relation * {@code (-1) * 0 = 0}. </p> * * @return {@code true} if negative and not empty else {@code false} */ boolean isNegative(); /** * <p>Queries if this time span is positive. </p> * * <p>A time span is positive if it is neither empty nor negative. </p> * * @return {@code true} if positive and not empty else {@code false} * @see #isEmpty() * @see #isNegative() */ /*[deutsch] * <p>Ist diese Zeitspanne positiv? </p> * * <p>Eine Zeitspanne ist genau dann positiv, wenn sie weder leer noch * negativ ist. </p> * * @return {@code true} if positive and not empty else {@code false} * @see #isEmpty() * @see #isNegative() */ boolean isPositive(); /** * <p>Queries if this time span is empty. </p> * * <p>Per definition an empty time span has no items with a partial * amount different from {@code 0}. </p> * * @return {@code true} if empty else {@code false} */ /*[deutsch] * <p>Liegt eine leere Zeitspanne vor? </p> * * <p>Per Definition hat eine leere Zeitspanne keine Elemente mit * einem von {@code 0} verschiedenen Teilbetrag. </p> * * @return {@code true} if empty else {@code false} */ boolean isEmpty(); /** * <p>Adds this time span to given time point. </p> * * <p>Is equivalent to the expression {@link TimePoint#plus(TimeSpan) * time.plus(this)}. Due to better readability usage of the * {@code TimePoint}-method is recommended. Implementations are * required to document the used algorithm in detailed manner. </p> * * @param <T> generic type of time point * @param time reference time point to add this time span to * @return new time point as result of addition * @see #subtractFrom(TimePoint) */ /*[deutsch] * <p>Addiert diese Zeitspanne zum angegebenen Zeitpunkt. </p> * * <p>Entspricht dem Ausdruck {@link TimePoint#plus(TimeSpan) * time.plus(this)}. Aus Gründen des besseren Lesestils empfiehlt * sich jedoch meistens die Verwendung der {@code TimePoint}-Methode. * Implementierungen müssen den verwendeten Algorithmus genau * dokumentieren. </p> * * @param <T> generic type of time point * @param time reference time point to add this time span to * @return new time point as result of addition * @see #subtractFrom(TimePoint) */ <T extends TimePoint<? super U, T>> T addTo(T time); /** * <p>Subtracts this time span from given time point. </p> * * <p>Is equivalent to the expression {@link TimePoint#minus(TimeSpan) * time.minus(this)}. Due to better readability usage of the * {@code TimePoint}-method is recommended. Implementations are * required to document the used algorithm in detailed manner. </p> * * @param <T> generic type of time point * @param time reference time point to subtract this time span from * @return new time point as result of subtraction * @see #addTo(TimePoint) */ /*[deutsch] * <p>Subtrahiert diese Zeitspanne vom angegebenen Zeitpunkt. </p> * * <p>Entspricht dem Ausdruck {@link TimePoint#minus(TimeSpan) * time.minus(this)}. Aus Gründen des besseren Lesestils empfiehlt * sich jedoch meistens die Verwendung der {@code TimePoint}-Methode. * Implementierungen müssen den verwendeten Algorithmus genau * dokumentieren. </p> * * @param <T> generic type of time point * @param time reference time point to subtract this time span from * @return new time point as result of subtraction * @see #addTo(TimePoint) */ <T extends TimePoint<? super U, T>> T subtractFrom(T time); //~ Innere Klassen ---------------------------------------------------- /** * <p>Represents a single item of a time span which is based on only one * time unit and has a non-negative amount. </p> * * @param <U> type of time unit * @doctags.concurrency {immutable} */ /*[deutsch] * <p>Repräsentiert ein atomares Element einer Zeitspanne, das auf nur * einer Zeiteinheit beruht und einen nicht-negativen Betrag hat. </p> * * @param <U> type of time unit * @doctags.concurrency {immutable} */ public static final class Item<U> implements Serializable { //~ Statische Felder/Initialisierungen ---------------------------- private static final long serialVersionUID = 1564804888291509484L; //~ Instanzvariablen ---------------------------------------------- /** * @serial time unit */ /*[deutsch] * @serial Zeiteinheit */ private final U unit; /** * @serial amount associated with a time unit {@code > 0} */ /*[deutsch] * @serial mit der Zeiteinheit assoziierter Betrag {@code > 0} */ private final long amount; //~ Konstruktoren ------------------------------------------------- private Item( long amount, U unit ) { super(); if (unit == null) { throw new NullPointerException("Missing chronological unit."); } else if (amount < 0) { throw new IllegalArgumentException( "Temporal amount must be positive or zero: " + amount); } this.amount = amount; this.unit = unit; } //~ Methoden ------------------------------------------------------ /** * <p>Creates a new time span item. </p> * * @param <U> type of time unit * @param amount amount in units {@code >= 0} * @param unit time unit * @return new time span item * @throws IllegalArgumentException if amount is negative */ /*[deutsch] * <p>Konstruiert ein neues Zeitspannenelement. </p> * * @param <U> type of time unit * @param amount amount in units {@code >= 0} * @param unit time unit * @return new time span item * @throws IllegalArgumentException if amount is negative */ public static <U> Item<U> of( long amount, U unit ) { return new Item<U>(amount, unit); } /** * <p>Yields the non-negative amount. </p> * * @return amount in units ({@code >= 0}) */ /*[deutsch] * <p>Liefert den nicht-negativen Betrag. </p> * * @return amount in units ({@code >= 0}) */ public long getAmount() { return this.amount; } /** * <p>Yields the time unit. </p> * * @return time unit */ /*[deutsch] * <p>Liefert die Zeiteinheit. </p> * * @return time unit */ public U getUnit() { return this.unit; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof Item) { Item<?> that = Item.class.cast(obj); return ( (this.amount == that.amount) && this.unit.equals(that.unit) ); } else { return false; } } @Override public int hashCode() { int hash = this.unit.hashCode(); hash = 29 * hash + (int) (this.amount ^ (this.amount >>> 32)); return hash; } /** * <p>Provides a canonical representation in the format * 'P' amount '{' unit '}', for example "P4{YEARS}". </p> * * @return String */ /*[deutsch] * <p>Liefert eine kanonische Darstellung im Format * 'P' amount '{' unit '}', zum Beispiel "P4{YEARS}". </p> * * @return String */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('P'); sb.append(this.amount); sb.append('{'); sb.append(this.unit); sb.append('}'); return sb.toString(); } /** * @serialData Checks the consistency. * @param in object input stream * @throws InvalidObjectException if the state is inconsistent * @throws ClassNotFoundException if class-loading fails */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); if ( (this.unit == null) || (this.amount < 0) ) { throw new InvalidObjectException("Inconsistent state."); } } } }