From bb16e8346c9f4358f44f0963c3999122e4ebfbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Wed, 19 Jan 2011 18:19:27 +0100 Subject: [PATCH] Add possibility of specifying new invalid dates The invalid ranges mechanism is not suitable when the invalid dates to specify are potentially infinite. An additional mechanism is added allowing to veto some of the dates. FEA: ItEr68OTS05IntroducionLimiteSobreasignacionCalendarios --- .../entities/AvailabilityTimeLine.java | 56 ++++++++++++++++- .../calendars/entities/BaseCalendar.java | 18 +++++- .../entities/AvailabilityTimeLineTest.java | 62 +++++++++++++++++++ .../calendars/entities/BaseCalendarTest.java | 10 +++ 4 files changed, 142 insertions(+), 4 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 53d36a19f..a26c4b13d 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 @@ -333,6 +333,10 @@ public class AvailabilityTimeLine { } } + public interface IVetoer { + public boolean isValid(LocalDate date); + } + public static AvailabilityTimeLine allValid() { return new AvailabilityTimeLine(); } @@ -343,18 +347,31 @@ public class AvailabilityTimeLine { return result; } + private static IVetoer NO_VETOER = new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return true; + } + }; + + private IVetoer vetoer = NO_VETOER; + private List invalids = new ArrayList(); private AvailabilityTimeLine() { } public boolean isValid(LocalDate date) { + return isValidBasedOnInvaidIntervals(date) && vetoer.isValid(date); + } + + private boolean isValidBasedOnInvaidIntervals(LocalDate date) { if (invalids.isEmpty()) { return true; } Interval possibleInterval = findPossibleIntervalFor(date); - return (possibleInterval == null || !possibleInterval.includes(date)) - && additionalRestriction.isValid(date); + return possibleInterval == null || !possibleInterval.includes(date); } private Interval findPossibleIntervalFor(LocalDate date) { @@ -380,6 +397,19 @@ public class AvailabilityTimeLine { insert(point); } + /** + * There are some invalid dates that cannot or are not suitable to be + * represented as belonging to invalid intervals. For example if the invalid + * dates are an infinite set. + * + * @param vetoer + * the vetoer to use + */ + public void setVetoer(IVetoer vetoer) { + Validate.notNull(vetoer); + this.vetoer = vetoer; + } + private void insert(Interval toBeInserted) { if (invalids.isEmpty()) { invalids.add(toBeInserted); @@ -481,9 +511,20 @@ public class AvailabilityTimeLine { AvailabilityTimeLine result = AvailabilityTimeLine.allValid(); inserting(result, invalids); inserting(result, another.invalids); + result.setVetoer(and(this.vetoer, another.vetoer)); return result; } + private static IVetoer and(final IVetoer a, + final IVetoer b) { + return new IVetoer() { + @Override + public boolean isValid(LocalDate date) { + return a.isValid(date) && b.isValid(date); + } + }; + } + public AvailabilityTimeLine or(AvailabilityTimeLine another) { List intersections = doIntersections(this, another); AvailabilityTimeLine result = AvailabilityTimeLine.allValid(); @@ -502,9 +543,20 @@ public class AvailabilityTimeLine { FixedPoint.tryExtract(each.getEnd())); } } + result.setVetoer(or(this.vetoer, another.vetoer)); return result; } + private static IVetoer or(final IVetoer a, + final IVetoer b) { + return new IVetoer() { + @Override + public boolean isValid(LocalDate date) { + return a.isValid(date) || b.isValid(date); + } + }; + } + private static List doIntersections(AvailabilityTimeLine one, AvailabilityTimeLine another) { List result = new ArrayList(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java index 9f151363e..f12eedcf9 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java @@ -36,6 +36,7 @@ import org.hibernate.validator.NotNull; import org.hibernate.validator.Valid; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.IVetoer; import org.navalplanner.business.calendars.entities.CalendarData.Days; import org.navalplanner.business.common.IntegrationEntity; import org.navalplanner.business.common.entities.EntitySequence; @@ -789,10 +790,23 @@ public class BaseCalendar extends IntegrationEntity implements ICalendar { private void addInvaliditiesDerivedFromCalendar(AvailabilityTimeLine result) { addInvaliditiesFromAvailabilities(result); addInvaliditiesFromExceptions(result); - addInvaliditiesFromCalendarDatas(result); + addInvaliditiesFromEmptyCalendarDatas(result); + addInvaliditiesFromEmptyDaysInCalendarDatas(result); } - private void addInvaliditiesFromCalendarDatas(AvailabilityTimeLine result) { + private void addInvaliditiesFromEmptyDaysInCalendarDatas( + AvailabilityTimeLine result) { + result.setVetoer(new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return canWorkOn(date); + } + }); + } + + private void addInvaliditiesFromEmptyCalendarDatas( + AvailabilityTimeLine result) { LocalDate previous = null; for (CalendarData each : calendarDataVersions) { addInvalidityIfDataEmpty(result, previous, 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 8e38f6fff..290a1af8e 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 @@ -20,6 +20,7 @@ */ package org.navalplanner.business.test.calendars.entities; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -38,6 +39,7 @@ import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.DatePoint; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.EndOfTime; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.FixedPoint; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.IVetoer; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.Interval; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.StartOfTime; @@ -300,6 +302,36 @@ public class AvailabilityTimeLineTest { .plusDays(20)), EndOfTime.create())); } + @Test + public void doingAnORDoesAnOrForAdditionalConstraints() { + boolean[][] validities = { { true, true }, { true, false }, + { false, true }, { false, false } }; + for (final boolean[] pairs : validities) { + AvailabilityTimeLine a = AvailabilityTimeLine.allValid(); + a.setVetoer(new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return pairs[0]; + } + }); + AvailabilityTimeLine b = AvailabilityTimeLine.allValid(); + b.setVetoer(new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return pairs[1]; + } + }); + AvailabilityTimeLine result = a.or(b); + boolean expected = pairs[0] || pairs[1]; + + assertThat(result.isValid(earlyExample), equalTo(expected)); + assertThat(result.isValid(contemporaryExample), equalTo(expected)); + assertThat(result.isValid(lateExample), equalTo(expected)); + } + } + @Test public void doingAnAndWithAnAllValidTimeLineProducesTheSameTimeLine() { AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid(); @@ -315,6 +347,36 @@ public class AvailabilityTimeLineTest { .plusDays(20)), EndOfTime.create())); } + @Test + public void doingAnAndIntersectsTheAdditionalConstraints() { + boolean[][] validities = { { true, true }, { true, false }, + { false, true }, { false, false } }; + for (final boolean[] pairs : validities) { + AvailabilityTimeLine a = AvailabilityTimeLine.allValid(); + a.setVetoer(new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return pairs[0]; + } + }); + AvailabilityTimeLine b = AvailabilityTimeLine.allValid(); + b.setVetoer(new IVetoer() { + + @Override + public boolean isValid(LocalDate date) { + return pairs[1]; + } + }); + AvailabilityTimeLine result = a.and(b); + boolean expected = pairs[0] && pairs[1]; + + assertThat(result.isValid(earlyExample), equalTo(expected)); + assertThat(result.isValid(contemporaryExample), equalTo(expected)); + assertThat(result.isValid(lateExample), equalTo(expected)); + } + } + @Test public void doingAnOrWithANeverValidTimeLineProducesTheSameTimeLine() { AvailabilityTimeLine timeLine = AvailabilityTimeLine.allValid(); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java index 07afd2202..18033a90e 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java @@ -35,6 +35,7 @@ import java.util.Set; import org.joda.time.LocalDate; import org.junit.Test; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.calendars.entities.BaseCalendar.DayType; import org.navalplanner.business.calendars.entities.CalendarData.Days; @@ -909,4 +910,13 @@ public class BaseCalendarTest { assertFalse(calendar.canWorkOn(MONDAY_LOCAL_DATE)); } + @Test + public void theAvailabilityTimeLineTakesIntoAccountTheDaysItCannotWorkDueToCalendarData() { + BaseCalendar calendar = createBasicCalendar(); + calendar.setCapacityAt(Days.MONDAY, Capacity.create(hours(0)) + .overAssignableWithoutLimit(false)); + + AvailabilityTimeLine availability = calendar.getAvailability(); + assertFalse(availability.isValid(MONDAY_LOCAL_DATE)); + } }