/* * ----------------------------------------------------------------------- * Copyright © 2013-2015 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (StdOperator.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; /** * <p>Factory for producing standard chronological operators which are applicable * on most chronological entities. </p> * * @param <T> generic target type of operator * @author Meno Hochschild * @since 3.5/4.3 */ /*[deutsch] * <p>Standard-Operator, der auf die meisten chronologischen Entitäten anwendbar ist. </p> * * @param <T> generic target type of operator * @author Meno Hochschild * @since 3.5/4.3 */ public class StdOperator<T extends ChronoEntity<T>> implements ChronoOperator<T> { //~ Statische Felder/Initialisierungen -------------------------------- private static final int NEW_VALUE_MODE = 0; private static final int MIN_MODE = 1; private static final int MAX_MODE = 2; private static final int FLOOR_MODE = 3; private static final int CEILING_MODE = 4; private static final int LENIENT_MODE = 5; private static final int DECREMENTING_MODE = 6; private static final int INCREMENTING_MODE = 7; //~ Instanzvariablen ---------------------------------------------- private final int mode; private final ChronoElement<?> element; private final Object value; //~ Konstruktoren ------------------------------------------------- private StdOperator( int mode, ChronoElement<?> element ) { this(mode, element, null); } private StdOperator( int mode, ChronoElement<?> element, Object value // optional ) { super(); if (element == null) { throw new NullPointerException("Missing chronological element."); } this.mode = mode; this.element = element; this.value = value; } //~ Methoden ------------------------------------------------------ @Override public T apply(T entity) { switch (this.mode) { case NEW_VALUE_MODE: return value(entity, this.element, this.value, false); case MIN_MODE: return min(entity, this.element); case MAX_MODE: return max(entity, this.element); case FLOOR_MODE: return floor(entity, this.element); case CEILING_MODE: return ceiling(entity, this.element); case LENIENT_MODE: return value(entity, this.element, this.value, true); case DECREMENTING_MODE: return this.move(entity, false); case INCREMENTING_MODE: return this.move(entity, true); default: throw new UnsupportedOperationException( "Unknown mode: " + this.mode); } } /** * <p>Yields a new operator which can set any entity to its minimum * element value. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität auf * das Elementminimum setzt. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> minimized(ChronoElement<?> element) { return new StdOperator<>(MIN_MODE, element); } /** * <p>Yields a new operator which can set any entity to its maximum * element value. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität auf * das Elementmaximum setzt. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> maximized(ChronoElement<?> element) { return new StdOperator<>(MAX_MODE, element); } /** * <p>Yields a new operator which can set any entity such that its * actual element value gets the decremented value. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @see TimeAxis#getBaseUnit(ChronoElement) * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität so * anpasst, daß dieses Element den vorherigen Wert bekommt. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @see TimeAxis#getBaseUnit(ChronoElement) * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> decremented(ChronoElement<?> element) { return new StdOperator<>(DECREMENTING_MODE, element); } /** * <p>Yields a new operator which can set any entity such that its * actual element value gets the incremented value. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @see TimeAxis#getBaseUnit(ChronoElement) * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität so * anpasst, daß dieses Element den nächsten Wert bekommt. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @see TimeAxis#getBaseUnit(ChronoElement) * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> incremented(ChronoElement<?> element) { return new StdOperator<>(INCREMENTING_MODE, element); } /** * <p>Yields an operator which rounds any entity down so that the child * elements will be set to the minimum. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine Entität abrundet, indem alle * Kindselemente dieses Elements auf ihr Minimum gesetzt werden. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> atFloor(ChronoElement<?> element) { return new StdOperator<>(FLOOR_MODE, element); } /** * <p>Yields an operator which rounds any entity up so that the child * elements will be set to the maximum. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine Entität aufrundet, indem alle * Kindselemente dieses Elements auf ihr Maximum gesetzt werden. </p> * * @param <T> generic type of target entity * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>> ChronoOperator<T> atCeiling(ChronoElement<?> element) { return new StdOperator<>(CEILING_MODE, element); } /** * <p>Yields an operator which sets any entity such that its actual * element value will be set in lenient mode to given value. </p> * * @param <T> generic type of target entity * @param <V> generic element value type * @param value new element value * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität so * anpasst, daß dieses Element auf den angegebenen Wert im * Nachsichtigkeitsmodus gesetzt wird. </p> * * @param <T> generic type of target entity * @param <V> generic element value type * @param value new element value * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>, V> ChronoOperator<T> setLenient( V value, ChronoElement<V> element ) { return new StdOperator<>(LENIENT_MODE, element, value); } /** * <p>Yields an operator which sets any entity such that its actual * element value will be set in normal mode to given value. </p> * * @param <T> generic type of target entity * @param <V> generic element value type * @param value new element value * @param element associated chronological element * @return operator * @since 3.5/4.3 */ /*[deutsch] * <p>Liefert einen Operator, der eine beliebige Entität so * anpasst, daß dieses Element auf den angegebenen Wert im * Standardmodus gesetzt wird. </p> * * @param <T> generic type of target entity * @param <V> generic element value type * @param value new element value * @param element associated chronological element * @return operator * @since 3.5/4.3 */ public static <T extends ChronoEntity<T>, V> ChronoOperator<T> newValue( V value, ChronoElement<V> element ) { return new StdOperator<>(NEW_VALUE_MODE, element, value); } private <V> T min( ChronoEntity<T> entity, ChronoElement<V> element ) { return entity.with(element, entity.getMinimum(element)); } private <V> T max( ChronoEntity<T> entity, ChronoElement<V> element ) { return entity.with(element, entity.getMaximum(element)); } private <V> T floor( ChronoEntity<T> entity, ChronoElement<V> element ) { T ctx = entity.getContext(); ChronoElement<?> e = element; while ((e = ctx.getChronology().getRule(e).getChildAtFloor(ctx)) != null) { ctx = withFloor(ctx, e); } return ctx; } private <V> T ceiling( ChronoEntity<T> entity, ChronoElement<V> element ) { T ctx = entity.getContext(); ChronoElement<?> e = element; while ((e = ctx.getChronology().getRule(e).getChildAtCeiling(ctx)) != null) { ctx = withCeiling(ctx, e); } return ctx; } private <V> T value( ChronoEntity<T> entity, ChronoElement<V> element, Object value, boolean lenient ) { T ctx = entity.getContext(); return ctx.getChronology().getRule(element).withValue( ctx, element.getType().cast(value), lenient ); } private <V> T withFloor( T context, ChronoElement<V> element ) { ElementRule<T, V> rule = context.getChronology().getRule(element); return rule.withValue( context, rule.getMinimum(context), element.isLenient() ); } private <V> T withCeiling( T context, ChronoElement<V> element ) { ElementRule<T, V> rule = context.getChronology().getRule(element); return rule.withValue( context, rule.getMaximum(context), element.isLenient() ); } private T move( T entity, boolean forward ) { if (entity instanceof TimePoint) { TimePoint<?, ?> tp = TimePoint.class.cast(entity); Object answer = add(tp, this.element, forward); return entity.getChronology().getChronoType().cast(answer); } else { throw new ChronoException( "Base units not supported by: " + entity.getChronology().getChronoType()); } } // wildcard capture private static <U, T extends TimePoint<U, T>> T add( TimePoint<U, T> context, ChronoElement<?> element, boolean forward ) { U unit = context.getChronology().getBaseUnit(element); if (forward) { return context.plus(1, unit); } else { return context.minus(1, unit); } } }