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

net.objectlab.kit.datecalc.common.AbstractDateCalculator Maven / Gradle / Ivy

There is a newer version: 1.4.8
Show newest version
/*
 * ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
 *
 * Based in London, we are world leaders in the design and development
 * of bespoke applications for the securities financing markets.
 *
 * Click here to learn more
 *           ___  _     _           _   _          _
 *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
 *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
 *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
 *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
 *                   |__/
 *
 *                     www.ObjectLab.co.uk
 *
 * $Id$
 *
 * Copyright 2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.objectlab.kit.datecalc.common;

import static net.objectlab.kit.datecalc.common.HolidayHandlerType.BACKWARD;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.FORWARD;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.MODIFIED_FOLLOWING;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.MODIFIED_PRECEDING;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Abstract implementation in order to encapsulate all the common functionality
 * between Jdk and Joda implementations. It is parameterized on <E>
 * but basically Date and LocalDate are the only
 * viable values for it for now.
 *
 * @author Marcin Jekot and Benoit Xhenseval
 *
 * @param 
 *            a representation of a date, typically JDK: Date, Calendar;
 *            Joda:LocalDate, YearMonthDay
 *
 */
public abstract class AbstractDateCalculator implements DateCalculator {
    private static final int MONTHS_IN_YEAR = 12;

    protected static final int DAYS_IN_WEEK = 7;

    private String name;

    private E startDate;

    private E currentBusinessDate;

    private HolidayCalendar holidayCalendar;

    private HolidayHandler holidayHandler;

    private int currentIncrement = 0;

    protected AbstractDateCalculator(final String name, final HolidayCalendar holidayCalendar, final HolidayHandler holidayHandler) {
        this.name = name;
        if (holidayCalendar != null) {
            this.holidayCalendar = new ImmutableHolidayCalendar(holidayCalendar);
        } else {
            this.holidayCalendar = new ImmutableHolidayCalendar(new DefaultHolidayCalendar());
        }
        this.holidayHandler = holidayHandler;
    }

    public DateCalculator setHolidayCalendar(final HolidayCalendar calendar) {
        if (calendar != null) {
            if (calendar instanceof ImmutableHolidayCalendar) {
                holidayCalendar = calendar;
            } else {
                holidayCalendar = new ImmutableHolidayCalendar(calendar);
            }
        } else {
            holidayCalendar = new ImmutableHolidayCalendar(new DefaultHolidayCalendar());
        }
        return this;
    }

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public E getStartDate() {
        if (startDate == null) {
            startDate = getToday();
        }
        return startDate;
    }

    /** Set both start date and current date */
    public DateCalculator setStartDate(final E startDate) {
        this.startDate = startDate;
        setCurrentBusinessDate(startDate);
        return this;
    }

    public E getCurrentBusinessDate() {
        if (currentBusinessDate == null) {
            currentBusinessDate = getToday();
        }
        return currentBusinessDate;
    }

    /**
     * move the current date by a given tenor, this means that if a date is
     * either a 'weekend' or holiday, it will be skipped acording to the holiday
     * handler and not count towards the number of days to move.
     *
     * @param tenor the tenor.
     * @param spotLag
     *            number of days to "spot" days, this can vary from one market
     *            to the other.
     * @return the current businessCalendar (so one can do
     *         calendar.moveByTenor(StandardTenor.T_2M).getCurrentBusinessDate();)
     */
    public DateCalculator moveByTenor(final Tenor tenor, final int spotLag) {
        if (tenor == null) {
            throw new IllegalArgumentException("Tenor cannot be null");
        }

        TenorCode tenorCode = tenor.getCode();
        if (tenorCode != TenorCode.OVERNIGHT && tenorCode != TenorCode.TOM_NEXT /*&& spotLag != 0*/) {
            // get to the Spot date first:
            moveToSpotDate(spotLag);
        }
        int unit = tenor.getUnits();
        if (tenorCode == TenorCode.WEEK) {
            tenorCode = TenorCode.DAY;
            unit *= DAYS_IN_WEEK;
        }

        if (tenorCode == TenorCode.YEAR) {
            tenorCode = TenorCode.MONTH;
            unit *= MONTHS_IN_YEAR;
        }

        return applyTenor(tenorCode, unit);
    }

    protected DateCalculator applyTenor(final TenorCode tenorCode, final int unit) {
        DateCalculator calc;
        // move by tenor
        switch (tenorCode) {
        case OVERNIGHT:
            calc = moveByDays(1);
            break;
        case TOM_NEXT: // it would have NOT moved by
            calc = moveByDays(1); // calculate Tomorrow
            calc = moveByDays(1); // then the next!
            break;
        case SPOT_NEXT:
            calc = moveByDays(1);
            break;
        case SPOT:
            calc = this;
            break;
        case DAY:
            calc = moveByDays(unit);
            break;
        case MONTH:
            calc = moveByMonths(unit);
            break;
        default:
            throw new UnsupportedOperationException("Sorry not yet...");
        }
        return calc;
    }

    /**
     * Move the current date by a given tenor, please note that all tenors are
     * relative to the CURRENT day (and NOT from spot).
     *
     * @param tenor
     *            the Tenor to reach.
     * @return the current DateCalculator
     * @since 1.1.0
     */
    public DateCalculator moveByTenor(final Tenor tenor) {
        return moveByTenor(tenor, 0);
    }

    /**
     * Calculate a series of Tenor codes in one go based on current day,
     * this does NOT change the current business date.
     *
     * @return list of dates in same order as tenors.
     * @since 1.1.0
     */
    public List calculateTenorDates(final List tenors) {
        return calculateTenorDates(tenors, 0);
    }

    /**
     * Calculate a series of Tenor codes in one go based on SPOT day (calculated
     * with the spot lag), this does NOT change the current business date.
     *
     * @return list of dates in same order as tenors.
     * @since 1.1.0
     */
    public List calculateTenorDates(final List tenors, final int spotLag) {
        final List list = new ArrayList();

        if (tenors != null) {
            final E originalDate = clone(getCurrentBusinessDate());
            for (final Tenor tenor : tenors) {
                moveByTenor(tenor, spotLag);
                list.add(getCurrentBusinessDate());
                setCurrentBusinessDate(originalDate);
            }
        }

        return list;
    }

    // -----------------------------------------------------------------------
    //
    // ObjectLab, world leaders in the design and development of bespoke
    // applications for the securities financing markets.
    // www.ObjectLab.co.uk
    //
    // -----------------------------------------------------------------------

    protected abstract DateCalculator moveByMonths(int months);

    public DateCalculator setHolidayHandler(final HolidayHandler holidayHandler) {
        this.holidayHandler = holidayHandler;
        return this;
    }

    public String getHolidayHandlerType() {
        return holidayHandler != null ? holidayHandler.getType() : null;
    }

    /**
     * is the given date a non working day?
     */
    public boolean isNonWorkingDay(final E date) {
        if (date != null && (holidayCalendar.getEarlyBoundary() != null || holidayCalendar.getLateBoundary() != null)) {
            checkBoundary(date);
        }
        return isWeekend(date) || holidayCalendar.isHoliday(date);
    }

    /**
     * This may throw an {@link IndexOutOfBoundsException} if the date is not within the
     * boundaries.
     * @param date
     */
    protected abstract void checkBoundary(E date);

    public boolean isCurrentDateNonWorking() {
        if (currentBusinessDate == null) {
            currentBusinessDate = getToday();
        }
        return isNonWorkingDay(currentBusinessDate);
    }

    public E forceCurrentDateNoAdjustment(final E date) {
        currentBusinessDate = date;
        return currentBusinessDate;
    }

    public E setCurrentBusinessDate(final E date) {
        currentBusinessDate = date;
        if (holidayHandler != null && date != null) {
            currentBusinessDate = holidayHandler.moveCurrentDate(this);
        }
        if (date != null && (holidayCalendar.getEarlyBoundary() != null || holidayCalendar.getLateBoundary() != null)) {
            checkBoundary(date);
        }
        return currentBusinessDate;
    }

    public HolidayHandler getHolidayHandler() {
        return holidayHandler;
    }

    protected void moveToSpotDate(final int spotLag) {
        moveByBusinessDays(spotLag);
    }

    public DateCalculator moveByBusinessDays(final int businessDays) {
        checkHolidayValidity(businessDays);

        final int numberOfStepsLeft = Math.abs(businessDays);
        final int step = businessDays < 0 ? -1 : 1;

        for (int i = 0; i < numberOfStepsLeft; i++) {
            moveByDays(step);
        }

        return this;
    }

    private void checkHolidayValidity(final int businessDays) {
        if (businessDays > 0 && holidayHandler != null
                && (holidayHandler.getType().equals(BACKWARD) || holidayHandler.getType().equals(MODIFIED_PRECEDING))) {
            throw new IllegalArgumentException("A " + MODIFIED_PRECEDING + " or " + BACKWARD
                    + " does not allow positive steps for moveByBusinessDays");
        } else if (businessDays < 0 && holidayHandler != null
                && (holidayHandler.getType().equals(FORWARD) || holidayHandler.getType().equals(MODIFIED_FOLLOWING))) {
            throw new IllegalArgumentException("A " + MODIFIED_FOLLOWING + " or " + FORWARD + " does not allow negative steps for moveByBusinessDays");
        }
    }

    /**
     * Allows DateCalculators to be combined into a new one, the startDate and
     * currentBusinessDate will be the ones from the existing calendar (not the
     * parameter one). The name will be combined name1+"/"+calendar.getName().
     *
     * @param calculator
     *            return the same DateCalculator if calendar is null or the
     *            original calendar (but why would you want to do that?)
     * @throws IllegalArgumentException
     *             if both calendars have different types of HolidayHandlers or
     *             WorkingWeek;
     */
    public DateCalculator combine(final DateCalculator calculator) {
        if (calculator == null || calculator == this) {
            return this;
        }

        checkHolidayHandlerValidity(calculator);

        final Set newSet = new HashSet();
        if (holidayCalendar != null) {
            newSet.addAll(holidayCalendar.getHolidays());
        }

        final HolidayCalendar calendarToCombine = calculator.getHolidayCalendar();
        checkBoundaries(calendarToCombine);

        if (calendarToCombine.getHolidays() != null) {
            newSet.addAll(calendarToCombine.getHolidays());
        }

        final HolidayCalendar newCal = new DefaultHolidayCalendar(newSet, compareDate(holidayCalendar.getEarlyBoundary(),
                calendarToCombine.getEarlyBoundary(), false), compareDate(holidayCalendar.getLateBoundary(), calendarToCombine.getLateBoundary(),
                true));

        final DateCalculator cal = createNewCalculator(getName() + "/" + calculator.getName(), getStartDate(), newCal, holidayHandler);

        return cal;
    }

    private void checkHolidayHandlerValidity(final DateCalculator calculator) {
        if (holidayHandler == null && calculator.getHolidayHandlerType() != null || holidayHandler != null
                && !holidayHandler.getType().equals(calculator.getHolidayHandlerType())) {
            throw new IllegalArgumentException("Combined Calendars cannot have different handler types");
        }
    }

    private void checkBoundaries(final HolidayCalendar calendarToCombine) {
        if (calendarToCombine.getEarlyBoundary() != null && holidayCalendar.getEarlyBoundary() == null
                || calendarToCombine.getEarlyBoundary() == null && holidayCalendar.getEarlyBoundary() != null) {
            throw new IllegalArgumentException("Both Calendar to be combined must either have each Early boundaries or None.");
        }

        if (calendarToCombine.getLateBoundary() != null && holidayCalendar.getLateBoundary() == null || calendarToCombine.getLateBoundary() == null
                && holidayCalendar.getLateBoundary() != null) {
            throw new IllegalArgumentException("Both Calendar to be combined must either have each Late boundaries or None.");
        }
    }

    protected abstract E getToday();

    protected abstract E compareDate(E date1, E date2, boolean returnEarliest);

    protected abstract DateCalculator createNewCalculator(String calcName, E theStartDate, HolidayCalendar holidays, HolidayHandler handler);

    /**
     * @return Returns the currentIncrement.
     */
    public int getCurrentIncrement() {
        return currentIncrement;
    }

    /**
     * @param currentIncrement The currentIncrement to set.
     */
    public DateCalculator setCurrentIncrement(final int currentIncrement) {
        this.currentIncrement = currentIncrement;
        return this;
    }

    /**
     * @return Returns the holidayCalendar.
     */
    public HolidayCalendar getHolidayCalendar() {
        return holidayCalendar;
    }

    protected abstract E clone(final E date);
}

/*
 * ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
 *
 * Based in London, we are world leaders in the design and development
 * of bespoke applications for the securities financing markets.
 *
 * Click here to learn more about us
 *           ___  _     _           _   _          _
 *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
 *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
 *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
 *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
 *                   |__/
 *
 *                     www.ObjectLab.co.uk
 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy