/* * 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.timeline; import kr.debop4j.core.Guard; import kr.debop4j.core.tools.StringTool; import kr.debop4j.timeperiod.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * TimeLine 관련 Utility Class * * @author 배성혁 sunghyouk.bae@gmail.com * @since 13. 5. 16. 오전 11:24 */ public abstract class TimeLines { private static final Logger log = LoggerFactory.getLogger(TimeLines.class); private TimeLines() {} /** * {@link ITimeLineMomentCollection}의 모든 기간의 합집합을 구합니다. * * @param timeLineMoments the time line moments * @return the i time period collection */ public static ITimePeriodCollection combinePeriods(final ITimeLineMomentCollection timeLineMoments) { log.trace("ITimeLineMomentCollection에서 모든 기간의 합집합을 구합니다..."); ITimePeriodCollection periods = new TimePeriodCollection(); if (timeLineMoments.isEmpty()) return periods; int momentsSize = timeLineMoments.size(); int itemIndex = 0; while (itemIndex < momentsSize) { ITimeLineMoment periodStart = timeLineMoments.get(itemIndex); Guard.shouldBe(periodStart.getStartCount() > 0, "periodStart.getStartCount() > 0 이어야 합니다. startCount=[%d]", periodStart.getStartCount()); // search next period end // using balancing to handling overlapping periods int balance = periodStart.getStartCount(); ITimeLineMoment periodEnd = null; while (itemIndex < momentsSize - 1 && balance > 0) { itemIndex++; periodEnd = timeLineMoments.get(itemIndex); balance += periodEnd.getStartCount(); balance -= periodEnd.getEndCount(); } Guard.shouldNotBeNull(periodEnd, "periodEnd"); // touching if (periodEnd.getStartCount() > 0) { itemIndex++; continue; } if (itemIndex < momentsSize) { TimeRange period = new TimeRange(); period.setup(periodStart.getMoment(), periodEnd.getMoment()); log.trace("combine period를 추가합니다. period=[{}]", period); periods.add(period); } itemIndex++; } if (log.isDebugEnabled()) log.debug("기간을 결합했습니다. periods=[{}]", StringTool.listToString(periods)); return periods; } /** * {@link ITimeLineMomentCollection}의 모든 기간의 교집합을 구합니다. * * @param timeLineMoments the time line moments * @return the i time period collection */ public static ITimePeriodCollection intersectPeriods(final ITimeLineMomentCollection timeLineMoments) { log.trace("ITimeLineMomentCollection의 요소들의 모든 Period로부터 교집합에 해당하는 구간을 구합니다..."); ITimePeriodCollection periods = new TimePeriodCollection(); if (timeLineMoments.isEmpty()) return periods; int intersectionStart = -1; int balance = 0; for (int i = 0; i < timeLineMoments.size(); i++) { ITimeLineMoment moment = timeLineMoments.get(i); int startCount = moment.getStartCount(); int endCount = moment.getEndCount(); balance += startCount; balance -= endCount; // intersection is starting by a period start if (startCount > 0 && balance > 1 && intersectionStart < 0) { intersectionStart = i; continue; } // intersection is starting by a period end if (endCount > 0 && balance <= 1 && intersectionStart >= 0) { ITimePeriod period = new TimeRange(); period.setup(timeLineMoments.get(intersectionStart).getMoment(), moment.getMoment()); log.trace("Intersect period를 추가합니다. period=[{}]", period); periods.add(period); intersectionStart = -1; } } if (log.isDebugEnabled()) log.debug("ITimeLineMomentCollection으로부터 교집합에 해당하는 기간을 구했습니다. periods=[{}]", StringTool.listToString(periods)); return periods; } /** * {@link ITimeLineMomentCollection}이 가진 모든 {@link ITimePeriod}들의 Gap을 계산합니다. (여집합) * * @param timeLineMoments the time line moments * @param range the range * @return Gap에 해당하는 기간들의 컬렉션 */ public static ITimePeriodCollection calculateGap(ITimeLineMomentCollection timeLineMoments, ITimePeriod range) { log.trace("ITimeLineMomentCollection의 모든 ITimePeriod에 속하지 않는 Gap을 구합니다(여집합). range=[{}]", range); ITimePeriodCollection gaps = new TimePeriodCollection(); if (timeLineMoments.isEmpty()) return gaps; // find leading gap ITimeLineMoment periodStart = timeLineMoments.getMin(); if (periodStart != null && range.getStart().compareTo(periodStart.getMoment()) < 0) { ITimePeriod startingGap = new TimeRange(); startingGap.setup(range.getStart(), periodStart.getMoment()); log.trace("starting gap을 추가합니다... startingGap=[{}~{}]", startingGap.getStart(), startingGap.getEnd()); gaps.add(startingGap); } // find intermediated gap int itemIndex = 0; while (itemIndex < timeLineMoments.size()) { ITimeLineMoment moment = timeLineMoments.get(itemIndex); Guard.shouldNotBeNull(moment, "moment"); Guard.shouldBe(moment.getStartCount() > 0, "moment.getStartCount() 값이 0보다 커야합니다. moment=[%s]", moment); // search next gap start // use balancing to handle overlapping periods int balance = moment.getStartCount(); ITimeLineMoment gapStart = null; while (itemIndex < timeLineMoments.size() - 1 && balance > 0) { itemIndex++; gapStart = timeLineMoments.get(itemIndex); balance += gapStart.getStartCount(); balance -= gapStart.getEndCount(); } Guard.shouldNotBeNull(gapStart, "gapStart"); if (gapStart.getStartCount() > 0) { itemIndex++; continue; } // found a gap if (itemIndex < timeLineMoments.size() - 1) { ITimePeriod gap = new TimeRange(); gap.setup(gapStart.getMoment(), timeLineMoments.get(itemIndex + 1).getMoment()); log.trace("intermediated gap을 추가합니다. gap=[{}~{}]", gap.getStart(), gap.getEnd()); gaps.add(gap); } itemIndex++; } // find ending gap ITimeLineMoment periodEnd = timeLineMoments.getMax(); if (periodEnd != null && range.getEnd().compareTo(periodEnd.getMoment()) > 0) { ITimePeriod endingGap = new TimeRange(); endingGap.setup(periodEnd.getMoment(), range.getEnd()); log.trace("ending gap을 추가합니다. endingGap=[{}~{}]", endingGap.getStart(), endingGap.getEnd()); gaps.add(endingGap); } if (log.isDebugEnabled()) log.debug("ITimeLineMomentCollection에서 gap을 계산했습니다. gaps=[{}]", StringTool.listToString(gaps)); return gaps; } }