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
This commit is contained in:
Óscar González Fernández 2011-01-19 18:19:27 +01:00
parent 76fcb59dad
commit bb16e8346c
4 changed files with 142 additions and 4 deletions

View file

@ -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<Interval> invalids = new ArrayList<Interval>();
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<Interval> 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<Interval> doIntersections(AvailabilityTimeLine one,
AvailabilityTimeLine another) {
List<Interval> result = new ArrayList<Interval>();

View file

@ -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);

View file

@ -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();

View file

@ -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));
}
}