Java tutorial
/* * 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; }