From 25bf19c45375107de9bee85c6b433b0265c5746c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Sun, 28 Feb 2010 21:34:09 +0100 Subject: [PATCH] ItEr49S10CUVisualizacionResponsabilidadesTRaballoNaPlanificacionItEr48S10: Adding or operation to AvailabilityTimeLine --- .../entities/AvailabilityTimeLine.java | 88 ++++++++++++++++--- .../entities/AvailabilityTimeLineTest.java | 84 ++++++++++++++++++ 2 files changed, 161 insertions(+), 11 deletions(-) diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/AvailabilityTimeLine.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/AvailabilityTimeLine.java index 7d2494c9a..21af95658 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/AvailabilityTimeLine.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/AvailabilityTimeLine.java @@ -137,6 +137,11 @@ public class AvailabilityTimeLine { public String toString() { return date.toString(); } + + public static LocalDate tryExtract(DatePoint start) { + FixedPoint point = (FixedPoint) start; + return point.getDate(); + } } public static class EndOfTime extends DatePoint { @@ -299,6 +304,11 @@ public class AvailabilityTimeLine { && end.compareTo(other.start) >= 0; } + public Interval intersect(Interval other) { + Validate.isTrue(overlaps(other)); + return new Interval(max(start, other.start), min(end, other.end)); + } + public Interval coalesce(Interval other) { if (!overlaps(other)) { throw new IllegalArgumentException( @@ -361,37 +371,63 @@ public class AvailabilityTimeLine { } private void insert(Interval toBeInserted) { - int binarySearch = Collections.binarySearch(invalids, toBeInserted); if (invalids.isEmpty()) { invalids.add(toBeInserted); return; } - toBeInserted = coalesceWithAdjacent(insertionPoint(binarySearch), - toBeInserted); + toBeInserted = coalesceWithAdjacent(toBeInserted); int insertionPoint = insertBeforeAllAdjacent(toBeInserted); removeAdjacent(insertionPoint, toBeInserted); } + /** + * Returns the insertion position for the interval. Inserting the interval + * at that position guarantees that interval start is posterior or equal to + * any previous interval start. If the next interval start is equal to the + * interval, the length of the former is less than the latter + */ + private int findInsertionPosition(Interval interval) { + int binarySearch = Collections.binarySearch(invalids, interval); + return insertionPoint(binarySearch); + } + private int insertBeforeAllAdjacent(Interval toBeInserted) { - int n = Collections.binarySearch(invalids, toBeInserted); - int insertionPoint = insertionPoint(n); + int insertionPoint = findInsertionPosition(toBeInserted); invalids.add(insertionPoint, toBeInserted); return insertionPoint; } - private Interval coalesceWithAdjacent(int insertionPoint, - Interval toBeInserted) { + private Interval coalesceWithAdjacent(Interval toBeInserted) { Interval result = toBeInserted; + List adjacent = getAdjacent(toBeInserted); + for (Interval each : adjacent) { + result = result.coalesce(each); + } + return result; + } + + private List getAdjacent(Interval toBeInserted) { + final int insertionPoint = findInsertionPosition(toBeInserted); + List result = new ArrayList(); for (int i = insertionPoint; i >= 0 - && (i == invalids.size() || at(i).overlaps( - toBeInserted)); i--) { + && (i == invalids.size() || at(i).overlaps(toBeInserted)); i--) { if (i < invalids.size()) { - result = result.coalesce(at(i)); + result.add(at(i)); } } for (int i = insertionPoint; i < invalids.size() && at(i).overlaps(toBeInserted); i++) { - result = result.coalesce(at(i)); + result.add(at(i)); + } + return result; + } + + private List intersectWithAdjacent(Interval interval) { + List result = new ArrayList(); + List adjacent = getAdjacent(interval); + for (Interval each : adjacent) { + assert interval.overlaps(each); + result.add(interval.intersect(each)); } return result; } @@ -440,6 +476,36 @@ public class AvailabilityTimeLine { return result; } + public AvailabilityTimeLine or(AvailabilityTimeLine another) { + List intersections = doIntersections(this, another); + AvailabilityTimeLine result = AvailabilityTimeLine.allValid(); + for (Interval each : intersections) { + boolean fromStartOfTime = each.getStart().equals( + StartOfTime.create()); + boolean untilEndOfTime = each.getEnd().equals(EndOfTime.create()); + if (fromStartOfTime && untilEndOfTime) { + result.allInvalid(); + } else if (fromStartOfTime) { + result.invalidUntil(FixedPoint.tryExtract(each.getEnd())); + } else if (untilEndOfTime) { + result.invalidFrom(FixedPoint.tryExtract(each.getStart())); + } else { + result.invalidAt(FixedPoint.tryExtract(each.getStart()), + FixedPoint.tryExtract(each.getEnd())); + } + } + return result; + } + + private static List doIntersections(AvailabilityTimeLine one, + AvailabilityTimeLine another) { + List result = new ArrayList(); + for (Interval each : one.invalids) { + result.addAll(another.intersectWithAdjacent(each)); + } + return result; + } + private void inserting(AvailabilityTimeLine result, List invalid) { for (Interval each : invalid) { result.insert(each); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/AvailabilityTimeLineTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/AvailabilityTimeLineTest.java index b9da4449a..704899097 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/AvailabilityTimeLineTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/AvailabilityTimeLineTest.java @@ -234,6 +234,90 @@ public class AvailabilityTimeLineTest { } } + @Test + public void doingOROnTwoTimeLinesResultingOnAnAllValidTimeLine() { + AvailabilityTimeLine one = AvailabilityTimeLine.allValid(); + one.invalidAt(contemporaryExample.minusDays(40), contemporaryExample + .minusDays(20)); + + AvailabilityTimeLine another = AvailabilityTimeLine.allValid(); + another.invalidAt(contemporaryExample.minusDays(10), + contemporaryExample.plusDays(10)); + + AvailabilityTimeLine result = one.or(another); + + assertThat(result.getValidPeriods(), definedBy(StartOfTime.create(), + EndOfTime.create())); + } + + @Test + public void doingORonTwoTimeLinesWithSeveralIntersectingInvalidPeriods() { + AvailabilityTimeLine one = AvailabilityTimeLine.allValid(); + one.invalidAt(contemporaryExample.minusDays(40), contemporaryExample + .minusDays(20)); + one.invalidAt(contemporaryExample.plusDays(35), contemporaryExample + .plusDays(50)); + + AvailabilityTimeLine another = AvailabilityTimeLine.allValid(); + another.invalidAt(contemporaryExample.minusDays(25), + contemporaryExample.plusDays(10)); + another.invalidAt(contemporaryExample.plusDays(30), contemporaryExample + .plusDays(40)); + + AvailabilityTimeLine result = one.or(another); + + assertThat(result.getValidPeriods(), definedBy(StartOfTime.create(), + point(contemporaryExample.minusDays(25)), + point(contemporaryExample.minusDays(20)), + point(contemporaryExample.plusDays(35)), + point(contemporaryExample.plusDays(40)), EndOfTime.create())); + } + + @Test + public void doingOROnTheSameTimeLineResultsInTheSameTimeLine() { + AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid(); + timeLine.invalidAt(earlyExample, contemporaryExample); + timeLine.invalidAt(lateExample, lateExample.plusDays(20)); + + AvailabilityTimeLine result = timeLine.or(timeLine); + + assertThat(result.getValidPeriods(), definedBy(StartOfTime.create(), + point(earlyExample), point(contemporaryExample), + point(lateExample), point(lateExample + .plusDays(20)), EndOfTime.create())); + } + + @Test + public void doingAnAndWithAnAllValidTimeLineProducesTheSameTimeLine() { + AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid(); + timeLine.invalidAt(earlyExample, contemporaryExample); + timeLine.invalidAt(lateExample, lateExample.plusDays(20)); + + AvailabilityTimeLine result = timeLine.and(AvailabilityTimeLine + .allValid()); + + assertThat(result.getValidPeriods(), definedBy(StartOfTime.create(), + point(earlyExample), point(contemporaryExample), + point(lateExample), point(lateExample + .plusDays(20)), EndOfTime.create())); + } + + @Test + public void doingAnOrWithANeverValidTimeLineProducesTheSameTimeLine() { + AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid(); + timeLine.invalidAt(earlyExample, contemporaryExample); + timeLine.invalidAt(lateExample, lateExample.plusDays(20)); + AvailabilityTimeLine another = AvailabilityTimeLine.allValid(); + another.allInvalid(); + + AvailabilityTimeLine result = timeLine.and(another); + + assertThat(result.getValidPeriods(), definedBy(StartOfTime.create(), + point(earlyExample), point(contemporaryExample), + point(lateExample), point(lateExample.plusDays(20)), EndOfTime + .create())); + } + @Test public void anAllValidPeriodsGeneratesAnAllEncompassingInterval() { AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid();