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: AbstractDateCalculator.java 309 2010-03-23 21:01:49Z marchy $
 * 
 * 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 parametrized on 
 * but basically Date and LocalDate are the only
 * viable values for it for now.
 * 
 * @author Marcin Jekot and Benoit Xhenseval
 * @author $LastChangedBy: marchy $
 * @version $Revision: 309 $ $Date: 2010-03-23 17:01:49 -0400 (Tue, 23 Mar 2010) $
 * 
 * @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;
    }

    @SuppressWarnings("unchecked")
    public void setHolidayCalendar(final HolidayCalendar calendar) {
        if (calendar != null) {
            if (calendar instanceof ImmutableHolidayCalendar) {
                holidayCalendar = calendar;
            } else {
                holidayCalendar = new ImmutableHolidayCalendar(calendar);
            }
        } else {
            holidayCalendar = new ImmutableHolidayCalendar(new DefaultHolidayCalendar());
        }
    }

    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 void setStartDate(final E startDate) {
        this.startDate = startDate;
        setCurrentBusinessDate(startDate);
    }

    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:
            moveByBusinessDays(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);
    }

    private 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 date = clone(getCurrentBusinessDate());
            for (final Tenor tenor : tenors) {
                moveByTenor(tenor, spotLag);
                list.add(getCurrentBusinessDate());
                setCurrentBusinessDate(date);
            }
        }

        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 void setHolidayHandler(final HolidayHandler holidayHandler) {
        this.holidayHandler = holidayHandler;
    }

    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 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;
    }

    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 calender 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 void setCurrentIncrement(final int currentIncrement) {
        this.currentIncrement = currentIncrement;
    }

    /**
     * @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 - 2024 Weber Informatics LLC | Privacy Policy