kr.debop4j.timeperiod.calendars.CalendarDateAdd.java Source code

Java tutorial

Introduction

Here is the source code for kr.debop4j.timeperiod.calendars.CalendarDateAdd.java

Source

/*
 * Copyright 2011-2013 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 kr.debop4j.timeperiod.calendars;

import kr.debop4j.core.Guard;
import kr.debop4j.core.NotSupportException;
import kr.debop4j.core.Pair;
import kr.debop4j.core.tools.StringTool;
import kr.debop4j.timeperiod.*;
import kr.debop4j.timeperiod.timeline.TimeGapCalculator;
import kr.debop4j.timeperiod.timerange.WeekRange;
import kr.debop4j.timeperiod.tools.Durations;
import kr.debop4j.timeperiod.tools.TimeSpec;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.Duration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static kr.debop4j.core.Guard.shouldBe;
import static kr.debop4j.core.Guard.shouldNotBeNull;
import static org.joda.time.Duration.ZERO;

/**
 *  Calendar   ? (Duration)? ? ? ?? .
 *
 * @author ? sunghyouk.bae@gmail.com
 * @since 13. 5. 20.  9:36
 */
@Slf4j
public class CalendarDateAdd extends DateAdd {

    @Getter
    private final ITimeCalendar timeCalendar;

    /** ? ? */
    @Getter
    private final List<DayOfWeek> weekDays = new ArrayList<>();

    /**  ?  (, ,     */
    @Getter
    private final List<HourRangeInDay> workingHours = new ArrayList<>();

    /** ?? ? ?  (?? ?  ) */
    @Getter
    private final List<DayHourRange> workingDayHours = new ArrayList<>();

    public CalendarDateAdd() {
        this(TimeCalendar.getEmptyOffset());
    }

    public CalendarDateAdd(ITimeCalendar calendar) {

        shouldNotBeNull(calendar, "calendar");
        shouldBe(calendar.getStartOffset().isEqual(ZERO),
                "Calendar.StartOffset? Duration.ZERO ? .");
        shouldBe(calendar.getEndOffset().isEqual(ZERO), "Calendar.EndOffset? Duration.ZERO ? .");

        this.timeCalendar = calendar;
    }

    @Override
    public ITimePeriodCollection getIncludePeriods() {
        throw new NotSupportException("IncludePeriods ? .");
    }

    /**  (-)? working day . */
    public void addWorkingWeekDays() {
        addWeekDays(TimeSpec.Weekdays);
    }

    /** ? (-?)? working day . */
    public void addWeekendWeekDays() {
        addWeekDays(TimeSpec.Weekends);
    }

    private void addWeekDays(DayOfWeek... dayOfWeeks) {
        Collections.addAll(weekDays, dayOfWeeks);
    }

    /** start ? offset ?  ?? . */
    public DateTime add(DateTime start, Duration offset) {
        return add(start, offset, SeekBoundaryMode.Next);
    }

    @Override
    public DateTime add(DateTime start, Duration offset, SeekBoundaryMode seekBoundary) {

        log.trace("add. start [{}] + offset [{}]? ?? ... seekBoundary=[{}]", start, offset,
                seekBoundary);

        if (getWeekDays().size() == 0 && getExcludePeriods().size() == 0 && getWorkingHours().size() == 0)
            return start.plus(offset);

        Pair<DateTime, Duration> endPair = (offset.compareTo(ZERO) < 0)
                ? calculateEnd(start, Durations.negate(offset), SeekDirection.Backward, seekBoundary)
                : calculateEnd(start, offset, SeekDirection.Forward, seekBoundary);

        DateTime end = endPair.getV1();

        log.trace("add. start [{}] + offset [{}] => end=[{}] seekBoundary=[{}]", start, offset, end, seekBoundary);

        return end;
    }

    @Override
    public DateTime subtract(DateTime start, Duration offset, SeekBoundaryMode seekBoundary) {

        log.trace("subtract. start [{}] - offset [{}]? ?? ... seekBoundary=[{}]", start,
                offset, seekBoundary);

        if (getWeekDays().size() == 0 && getExcludePeriods().size() == 0 && getWorkingHours().size() == 0)
            return start.minus(offset);

        Pair<DateTime, Duration> endTuple = (offset.compareTo(ZERO) < 0)
                ? calculateEnd(start, Durations.negate(offset), SeekDirection.Forward, seekBoundary)
                : calculateEnd(start, offset, SeekDirection.Backward, seekBoundary);

        DateTime end = endTuple.getV1();

        log.trace("subtract. start [{}] - offset [{}] => end=[{}] seekBoundary=[{}]", start, offset, end,
                seekBoundary);

        return end;
    }

    /**
     *  ? offset ?  ?? .
     *
     * @param start         ?
     * @param offset       
     * @param seekDir      ? 
     * @param seekBoundary   ? 
     * @return  ? ?  ?,  
     */
    @Override
    protected Pair<DateTime, Duration> calculateEnd(DateTime start, Duration offset, SeekDirection seekDir,
            SeekBoundaryMode seekBoundary) {

        log.trace(
                "? offset ?  ?? ... start=[{}], offset=[{}], seekDir=[{}], seekBoundary=[{}]",
                start, offset, seekDir, seekBoundary);
        Guard.shouldBe(offset.compareTo(ZERO) >= 0, "offset ? 0 ?? ? . offset=[%d]",
                offset.getMillis());

        DateTime end = null;
        DateTime moment = start;
        Duration remaining = offset;

        WeekRange week = new WeekRange(start, getTimeCalendar());

        while (week != null) {
            super.getIncludePeriods().clear();
            super.getIncludePeriods().addAll(getAvailableWeekPeriods(week));

            log.trace(" =[{}]", StringTool.listToString(super.getIncludePeriods()));

            Pair<DateTime, Duration> result = super.calculateEnd(moment, remaining, seekDir, seekBoundary);
            end = result.getV1();
            remaining = result.getV2();

            log.trace("? . end=[{}], remaining=[{}]", end, remaining);

            if (end != null || remaining == null)
                break;

            if (seekDir == SeekDirection.Forward) {
                week = findNextWeek(week);
                if (week != null)
                    moment = week.getStart();
            } else {
                week = findPreviousWeek(week);
                if (week != null)
                    moment = week.getEnd();
            }
        }

        log.trace(
                "? offset ?  ?? . start=[{}], offset=[{}], seekDir=[{}], seekBoundary=[{}], end=[{}], remaining=[{}]",
                start, offset, seekDir, seekBoundary, end, remaining);

        return Pair.create(end, remaining);
    }

    /**
     * current    ?  ?   WeekRange .
     *
     * @param current  (Week)
     * @return ?
     */
    private WeekRange findNextWeek(WeekRange current) {

        log.trace(" week[{}]? ? week ? ...", current);

        WeekRange next = null;

        if (getExcludePeriods().size() == 0) {
            next = current.nextWeek();
        } else {
            TimeRange limits = new TimeRange(current.getEnd().plusMillis(1), (DateTime) null);
            TimeGapCalculator<TimeRange> gapCalculator = new TimeGapCalculator<>(getTimeCalendar());
            ITimePeriodCollection remainingPeriods = gapCalculator.getGaps(getExcludePeriods(), limits);

            next = (remainingPeriods.size() > 0)
                    ? new WeekRange(remainingPeriods.get(0).getStart(), getTimeCalendar())
                    : null;
        }

        log.trace(" week[{}]? ? week  [{}] ", current, next);

        return next;
    }

    /**
     * current    ?  ?   WeekRange .
     *
     * @param current  (Week)
     * @return 
     */
    private WeekRange findPreviousWeek(WeekRange current) {

        log.trace(" week[{}]? ? week ? ...", current);

        WeekRange previous = null;

        if (getExcludePeriods().size() == 0) {
            previous = current.previousWeek();
        } else {
            TimeRange limits = new TimeRange(TimeSpec.MinPeriodTime, current.getStart().plusMillis(-1));
            TimeGapCalculator<TimeRange> gapCalculator = new TimeGapCalculator<>(getTimeCalendar());
            ITimePeriodCollection remainingPeriods = gapCalculator.getGaps(getExcludePeriods(), limits);

            previous = (remainingPeriods.size() > 0)
                    ? new WeekRange(remainingPeriods.get(remainingPeriods.size() - 1).getEnd(), getTimeCalendar())
                    : null;
        }

        log.trace(" week[{}]? ? week  [{}] ", current, previous);

        return previous;
    }

    /**
     *   ?  ?  ? HourRange   .
     *
     * @param limits  
     * @return  ?  
     */
    private Iterable<ITimePeriod> getAvailableWeekPeriods(ITimePeriod limits) {
        shouldNotBeNull(limits, "limits");

        log.trace(" ? ...  =[{}]", limits);

        if (weekDays.size() == 0 && workingHours.size() == 0 && workingDayHours.size() == 0) {
            TimePeriodCollection result = new TimePeriodCollection();
            result.add(limits);
            return result;
        }

        CalendarPeriodCollectorFilter filter = new CalendarPeriodCollectorFilter();
        filter.getWeekDays().addAll(weekDays);
        filter.getCollectingHours().addAll(workingHours);
        filter.getCollectingDayHours().addAll(workingDayHours);

        CalendarPeriodCollector weekCollector = new CalendarPeriodCollector(filter, limits, SeekDirection.Forward,
                getTimeCalendar());
        weekCollector.collectHours();

        return weekCollector.getPeriods();
    }

    private static final long serialVersionUID = -2499923637191503226L;
}