From 668a449b412e8687c5871ebaba387e88870cdfee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Mon, 8 Jun 2009 10:59:31 +0200 Subject: [PATCH] ItEr11S13ProbasModuloRecursos: Introducing tests that reproduce the detected bugs and fixes to resolve them. Now the implementation is much more simpler.CriterionSatisfaction can now be used more easily in forms. --- .../resources/entities/Criterion.java | 5 +- .../entities/CriterionSatisfaction.java | 75 +++- .../resources/entities/CriterionTypeBase.java | 12 +- .../resources/entities/ICriterionType.java | 2 +- .../business/resources/entities/Interval.java | 157 +++++++ .../entities/PredefinedCriterionTypes.java | 10 +- .../business/resources/entities/Resource.java | 413 +++++++++++------- .../services/impl/CriterionServiceImpl.java | 7 +- .../entities/CriterionSatisfactionTest.java | 94 +++- .../test/resources/entities/IntervalTest.java | 149 +++++++ .../test/resources/entities/ResourceTest.java | 271 +++++++----- .../services/CriterionServiceTest.java | 48 +- .../services/ResourceServiceTest.java | 2 +- .../resources/criterion/CriterionsModel.java | 6 +- .../worker/WorkRelationshipsController.java | 99 ++--- .../worker/WorkerCRUDController.java | 60 ++- .../web/resources/worker/WorkerModel.java | 14 +- .../worker/_editWorkRelationship.zul | 11 +- 18 files changed, 997 insertions(+), 438 deletions(-) create mode 100644 navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Interval.java create mode 100644 navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/IntervalTest.java diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java index 868fa6b95..3fca9d4ca 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java @@ -55,11 +55,12 @@ public class Criterion implements ICriterion { @Override public boolean isSatisfiedBy(Resource resource) { - return !resource.getActiveSatisfactionsFor(this).isEmpty(); + return !resource.getCurrentSatisfactionsFor(this).isEmpty(); } public boolean isSatisfiedBy(Resource resource, Date start, Date end) { - return !resource.getActiveSatisfactionsForIn(this, start, end) + return !resource.query().from(this) + .enforcedInAll(Interval.range(start, end)).result() .isEmpty(); } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java index 40ab932f9..3d3aaaf97 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionSatisfaction.java @@ -1,11 +1,9 @@ package org.navalplanner.business.resources.entities; +import java.util.Comparator; import java.util.Date; import org.apache.commons.lang.Validate; -import org.navalplanner.business.common.exceptions.InstanceNotFoundException; -import org.navalplanner.business.resources.daos.ICriterionSatisfactionDAO; -import org.springframework.beans.factory.annotation.Autowired; /** * Declares a interval of time in which the criterion is satisfied
@@ -13,18 +11,24 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class CriterionSatisfaction { + public static final Comparator BY_START_COMPARATOR; + + static { + BY_START_COMPARATOR = new Comparator() { + + @Override + public int compare(CriterionSatisfaction o1, + CriterionSatisfaction o2) { + return o1.getStartDate().compareTo(o2.getStartDate()); + } + }; + } + private Long id; @SuppressWarnings("unused") private long version; - @Autowired - private ICriterionSatisfactionDAO criterionSatisfactionDAO; - - - /** - * Required by hibernate. Do not use directly - */ public CriterionSatisfaction() { } @@ -39,6 +43,14 @@ public class CriterionSatisfaction { this.resource = resource; } + public CriterionSatisfaction(Criterion criterion, Resource resource, + Interval interval) { + this(interval.getStart(), criterion, resource); + if (interval.getEnd() != null) { + this.finish(interval.getEnd()); + } + } + public Long getId() { return id; } @@ -52,44 +64,54 @@ public class CriterionSatisfaction { private Resource resource; public Date getStartDate() { - return startDate==null ? null: new Date(startDate.getTime()); + return startDate != null ? new Date(startDate.getTime()) : null; } public Date getEndDate() { - if (isFinished() ) { + if (isFinished()) { return new Date(finishDate.getTime()); } else { return null; } } + public Interval getInterval() { + return Interval.range(startDate, finishDate); + } + public Criterion getCriterion() { return criterion; } + public void setCriterion(Criterion criterion) { + this.criterion = criterion; + } + public Resource getResource() { return resource; } - public boolean isActiveNow() { + public void setResource(Resource resource) { + this.resource = resource; + } + + public boolean isCurrent() { Date now = new Date(); - return isActiveAt(now); + return isEnforcedAt(now); } - public boolean isActiveAt(Date date) { - return (startDate.before(date) || startDate.equals(date)) - && (finishDate == null || date.before(finishDate)); + public boolean isEnforcedAt(Date date) { + return getInterval().contains(date); } - public boolean isActiveIn(Date start, Date end) { - return (startDate.equals(start) || startDate.before(start)) - && (finishDate == null || end.before(finishDate)); + public boolean isAlwaysEnforcedIn(Interval interval) { + return getInterval().includes(interval); } public void finish(Date finish) { Validate.notNull(finish); Validate.isTrue(startDate.equals(finish) || startDate.before(finish)); - finishDate = finish; + this.finishDate = finish; } public boolean isFinished() { @@ -97,13 +119,20 @@ public class CriterionSatisfaction { } public void setEndDate(Date date) { - if ( (startDate.equals(date) || startDate.before(date)) ) + if (date == null) { + finishDate = null; + } + if ((startDate.equals(date) || startDate.before(date))) finishDate = date; } public void setStartDate(Date date) { - if ( (finishDate == null || finishDate.after(date)) ) + if ((finishDate == null || finishDate.after(date))) startDate = date; } + public boolean overlapsWith(Interval interval) { + return getInterval().overlapsWith(interval); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionTypeBase.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionTypeBase.java index 413afa12f..0d6803a53 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionTypeBase.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionTypeBase.java @@ -10,7 +10,7 @@ public abstract class CriterionTypeBase implements ICriterionType { private final boolean allowHierarchy; - private final boolean allowMultipleCriterionsPerResource; + private final boolean allowSimultaneousCriterionsPerResource; private final String name; @@ -19,11 +19,11 @@ public abstract class CriterionTypeBase implements ICriterionType { private final boolean allowEditing; protected CriterionTypeBase(String name, boolean allowHierarchy, - boolean allowMultipleCriterionsPerResource, boolean allowAdding, - boolean allowEditing) { + boolean allowSimultaneousCriterionsPerResource, + boolean allowAdding, boolean allowEditing) { Validate.notNull(name, "name is not null"); this.allowHierarchy = allowHierarchy; - this.allowMultipleCriterionsPerResource = allowMultipleCriterionsPerResource; + this.allowSimultaneousCriterionsPerResource = allowSimultaneousCriterionsPerResource; this.name = name; this.allowAdding = allowAdding; this.allowEditing = allowEditing; @@ -35,8 +35,8 @@ public abstract class CriterionTypeBase implements ICriterionType { } @Override - public boolean allowMultipleActiveCriterionsPerResource() { - return allowMultipleCriterionsPerResource; + public boolean allowSimultaneousCriterionsPerResource() { + return allowSimultaneousCriterionsPerResource; } public String getName() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/ICriterionType.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/ICriterionType.java index a23d23cae..fca4140b6 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/ICriterionType.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/ICriterionType.java @@ -8,7 +8,7 @@ public interface ICriterionType { public String getName(); - public boolean allowMultipleActiveCriterionsPerResource(); + public boolean allowSimultaneousCriterionsPerResource(); public boolean allowHierarchy(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Interval.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Interval.java new file mode 100644 index 000000000..228b3d298 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Interval.java @@ -0,0 +1,157 @@ +package org.navalplanner.business.resources.entities; + +import java.util.Date; + +import org.apache.commons.lang.Validate; + +/** + * Represents a time interval
+ * @author Óscar González Fernández + */ +public abstract class Interval { + protected final Date start; + protected final Date end; + + public static Interval from(Date start) { + return new OpenEndedInterval(start); + } + + public static Interval point(Date date) { + return new Point(date); + } + + public static Interval range(Date start, Date end) { + Validate.notNull(start, "start date must be not null"); + if (end == null) + return from(start); + if (start.equals(end)) + return point(start); + return new Range(start, end); + } + + protected Interval(Date start, Date end) { + Validate.notNull(start, "start date must be not null"); + if (end != null) { + Validate.isTrue(start.compareTo(end) <= 0, + "start date must be equal or before than end date"); + } + this.start = start; + this.end = end; + } + + public abstract boolean contains(Date date); + + @Override + public boolean equals(Object obj) { + if (obj instanceof Interval) { + Interval interval = (Interval) obj; + return dateEquals(start, interval.start) + && dateEquals(end, interval.end); + } + return false; + } + + private boolean dateEquals(Date date1, Date date2) { + return date1 == date2 + || (date1 != null && date2 != null && date1.equals(date2)); + } + + public abstract boolean includes(Interval included); + + public abstract boolean overlapsWith(Interval interval); + + public boolean before(Date date) { + return start.before(date); + } + + public Date getStart() { + return new Date(start.getTime()); + } + + public Date getEnd() { + return end != null ? new Date(end.getTime()) : null; + } + +} + +class Range extends Interval { + + Range(Date start, Date end) { + super(start, end); + Validate.notNull(start); + Validate.notNull(end); + } + + @Override + public boolean contains(Date date) { + return date.compareTo(start) >= 0 && date.compareTo(end) < 0; + } + + @Override + public boolean includes(Interval included) { + if (included instanceof Point) { + Point point = (Point) included; + return point.overlapsWith(this); + } + return start.compareTo(included.start) <= 0 && included.end != null + && end.after(included.end); + } + + @Override + public boolean overlapsWith(Interval interval) { + if (interval instanceof Point) { + Point point = (Point) interval; + return point.overlapsWith(this); + } + return contains(interval.start) + || (interval.end != null ? contains(interval.end) + && !interval.end.equals(start) : end + .compareTo(interval.start) > 0); + } + +} + +class OpenEndedInterval extends Interval { + OpenEndedInterval(Date start) { + super(start, null); + } + + @Override + public boolean contains(Date date) { + return date.compareTo(start) >= 0; + } + + @Override + public boolean includes(Interval included) { + return start.compareTo(included.start) <= 0; + } + + @Override + public boolean overlapsWith(Interval interval) { + return start.before(interval.start) || interval.end == null + || start.before(interval.end); + } +} + +class Point extends Interval { + + Point(Date date) { + super(date, date); + } + + @Override + public boolean contains(Date date) { + return start.equals(date); + } + + @Override + public boolean includes(Interval included) { + return equals(included); + } + + @Override + public boolean overlapsWith(Interval interval) { + return interval.contains(end) && !interval.start.equals(end); + } + +} \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/PredefinedCriterionTypes.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/PredefinedCriterionTypes.java index 359d2ad91..c5fc037f7 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/PredefinedCriterionTypes.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/PredefinedCriterionTypes.java @@ -30,7 +30,7 @@ public enum PredefinedCriterionTypes implements ICriterionType { private final boolean allowHierarchy; - private final boolean allowMultipleActiveCriterionsPerResource; + private final boolean allowSimultaneousCriterionsPerResource; private final boolean allowAdding; @@ -39,11 +39,11 @@ public enum PredefinedCriterionTypes implements ICriterionType { private List> classes; private PredefinedCriterionTypes(boolean allowHierarchy, - boolean allowMultipleActiveCriterionsPerResource, + boolean allowSimultaneousCriterionsPerResource, boolean allowAdding, boolean allowEditing, Class... klasses) { this.allowHierarchy = allowHierarchy; - this.allowMultipleActiveCriterionsPerResource = allowMultipleActiveCriterionsPerResource; + this.allowSimultaneousCriterionsPerResource = allowSimultaneousCriterionsPerResource; this.allowAdding = allowAdding; this.allowEditing = allowEditing; this.classes = Arrays.asList(klasses); @@ -64,8 +64,8 @@ public enum PredefinedCriterionTypes implements ICriterionType { } @Override - public boolean allowMultipleActiveCriterionsPerResource() { - return allowMultipleActiveCriterionsPerResource; + public boolean allowSimultaneousCriterionsPerResource() { + return allowSimultaneousCriterionsPerResource; } @Override diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java index 5a57947a8..858fb47f0 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Resource.java @@ -1,11 +1,11 @@ package org.navalplanner.business.resources.entities; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -20,6 +20,7 @@ import org.navalplanner.business.resources.daos.ResourcesDaoRegistry; // child another simple resource, general methods like getChilds() do not make // sense for simple entities, etc.). In consequence, I prefer the modeling // option shown below. + /** * This class acts as the base class for all resources. * @author Fernando Bellas Permuy @@ -27,9 +28,126 @@ import org.navalplanner.business.resources.daos.ResourcesDaoRegistry; public abstract class Resource { private Long id; + private long version; + private Set criterionSatisfactions = new HashSet(); + private interface Predicate { + public boolean accepts(CriterionSatisfaction satisfaction); + } + + public class Query { + + private List predicates = new ArrayList(); + private boolean sort = false; + + private Query() { + + } + + public Query from(final ICriterionType type) { + return withNewPredicate(new Predicate() { + + @Override + public boolean accepts(CriterionSatisfaction satisfaction) { + return type.contains(satisfaction.getCriterion()); + } + }); + } + + private Query withNewPredicate(Predicate newPredicate) { + predicates.add(newPredicate); + return this; + } + + public Query at(Date date) { + return enforcedInAll(Interval.point(date)); + } + + public Query between(Date start, Date end) { + return enforcedInAll(Interval.range(start, end)); + } + + public Query enforcedInAll(final Interval interval) { + return withNewPredicate(new Predicate() { + + @Override + public boolean accepts(CriterionSatisfaction satisfaction) { + return satisfaction.isAlwaysEnforcedIn(interval); + } + }); + } + + public Query overlapsWith(final Interval interval) { + return withNewPredicate(new Predicate() { + + @Override + public boolean accepts(CriterionSatisfaction satisfaction) { + return satisfaction.overlapsWith(interval); + } + }); + } + + public Query from(final ICriterion criterion) { + return withNewPredicate(new Predicate() { + + @Override + public boolean accepts(CriterionSatisfaction satisfaction) { + return satisfaction.getCriterion().isEquivalent(criterion); + } + }); + } + + public Query sortByStartDate() { + sort = true; + return this; + } + + public List result() { + ArrayList result = new ArrayList(); + for (CriterionSatisfaction criterionSatisfaction : criterionSatisfactions) { + if (isAcceptedByAllPredicates(criterionSatisfaction)) { + result.add(criterionSatisfaction); + } + } + Collections.sort(result, CriterionSatisfaction.BY_START_COMPARATOR); + return result; + } + + private boolean isAcceptedByAllPredicates( + CriterionSatisfaction criterionSatisfaction) { + for (Predicate predicate : predicates) { + if (!predicate.accepts(criterionSatisfaction)) + return false; + } + return true; + } + + public Query current() { + return withNewPredicate(new Predicate() { + + @Override + public boolean accepts(CriterionSatisfaction satisfaction) { + return satisfaction.isCurrent(); + } + }); + } + + public List asCriterions() { + LinkedHashSet result = new LinkedHashSet(); + for (CriterionSatisfaction criterionSatisfaction : result()) { + result.add(criterionSatisfaction.getCriterion()); + } + return new ArrayList(result); + } + + } + + public Query query() { + return new Query(); + } + public Long getId() { return id; } @@ -63,207 +181,194 @@ public abstract class Resource { } public Set getAllSatisfactions() { - return new HashSet(criterionSatisfactions); + return new HashSet(criterionSatisfactions); } public Collection getSatisfactionsFor( ICriterionType type) { - Set allSatisfactions = getAllSatisfactions(); - ArrayList result = new ArrayList(); - for (CriterionSatisfaction criterionSatisfaction : allSatisfactions) { - if (type.contains(criterionSatisfaction.getCriterion())) { - result.add(criterionSatisfaction); - } - } - return result; + return query().from(type).result(); } public List getSatisfactionsFor(ICriterion criterion) { - ArrayList result = new ArrayList(); - for (CriterionSatisfaction criterionSatisfaction : getAllSatisfactions()) { - if (criterionSatisfaction.getCriterion().isEquivalent(criterion)) { - result.add(criterionSatisfaction); - } - } - return result; + return query().from(criterion).result(); } - public Set getActiveCriterionsFor(ICriterionType type) { - Set result = new HashSet(); - Collection active = getActiveSatisfactionsFor(type); - for (CriterionSatisfaction satisfaction : active) { - result.add(satisfaction.getCriterion()); - } - return result; + public List getCurrentCriterionsFor(ICriterionType type) { + return query().from(type).current().asCriterions(); } - public Collection getActiveSatisfactionsFor( + public Collection getCurrentSatisfactionsFor( ICriterionType criterionType) { - Collection satisfactionsFor = getSatisfactionsFor(criterionType); - ArrayList result = new ArrayList(); - for (CriterionSatisfaction criterionSatisfaction : satisfactionsFor) { - if (criterionSatisfaction.isActiveNow()) { - result.add(criterionSatisfaction); - } - } - return result; + return query().from(criterionType).current().result(); } - public Collection getActiveSatisfactionsForIn( - ICriterionType criterionType, Date start, Date end) { - Validate.notNull(criterionType); - Validate.isTrue(end == null || start.before(end)); - Collection satisfactionsFor = getSatisfactionsFor(criterionType); - ArrayList result = new ArrayList(); - for (CriterionSatisfaction criterionSatisfaction : satisfactionsFor) { - if (end == null && criterionSatisfaction.isActiveAt(start) || end != null && criterionSatisfaction.isActiveIn(start, end)) { - result.add(criterionSatisfaction); - } - } - return result; - } - - public Collection getActiveSatisfactionsAt( - ICriterionType criterionType, Date pointInTime) { - return getActiveSatisfactionsForIn(criterionType, pointInTime, null); - } - - public Collection getActiveSatisfactionsFor( + public List getCurrentSatisfactionsFor( ICriterion criterion) { - Set result = new HashSet(); - for (CriterionSatisfaction criterionSatisfaction : getAllSatisfactionsFor(criterion)) { - if (criterionSatisfaction.isActiveNow()) { - result.add(criterionSatisfaction); + return query().from(criterion).current().result(); + } + + public CriterionSatisfaction addSatisfaction( + CriterionWithItsType criterionWithItsType) { + return addSatisfaction(criterionWithItsType, Interval.from(new Date())); + } + + private static class EnsureSatisfactionIsCorrect { + + private EnsureSatisfactionIsCorrect(Resource resource, + ICriterionType type, CriterionSatisfaction satisfaction) { + Validate.notNull(resource); + Validate.notNull(satisfaction.getResource()); + Validate.notNull(satisfaction); + if (!satisfaction.getResource().equals(resource)) { + throw new IllegalArgumentException( + "the satisfaction is not related to this resource"); } + this.type = new CriterionWithItsType(type, satisfaction + .getCriterion()); + this.interval = satisfaction.getInterval(); + this.resource = resource; } - return result; - } - private Collection getAllSatisfactionsFor( - ICriterion criterion) { - Set result = new HashSet(); - for (CriterionSatisfaction satisfaction : criterionSatisfactions) { - if (satisfaction.getCriterion().isEquivalent(criterion)) { - result.add(satisfaction); - } + final Resource resource; + + final CriterionWithItsType type; + + final Interval interval; + + CriterionSatisfaction addSatisfaction() { + return resource.addSatisfaction(type, interval); } - return result; - } - public Collection getActiveSatisfactionsForIn( - ICriterion criterion, Date start, Date end) { - Validate.isTrue(end == null || start.before(end)); - ArrayList result = new ArrayList(); - Collection allSatisfactionsFor = getAllSatisfactionsFor(criterion); - for (CriterionSatisfaction criterionSatisfaction : allSatisfactionsFor) { - if (criterionSatisfaction.isActiveIn(start, end)) { - result.add(criterionSatisfaction); - } + boolean canAddSatisfaction() { + return resource.canAddSatisfaction(type, interval); } - return result; + } - public void activate(CriterionWithItsType criterionWithItsType) { - activate(criterionWithItsType, new Date()); + public CriterionSatisfaction addSatisfaction( + ICriterionType type, CriterionSatisfaction satisfaction) { + return new EnsureSatisfactionIsCorrect(this, type, satisfaction) + .addSatisfaction(); } - public void activate(CriterionWithItsType criterionWithItsType, Date start) { - activate(criterionWithItsType, start, null); - } - - public void activate(CriterionWithItsType criterionWithItsType, Date start, - Date finish) { - ICriterionType type = criterionWithItsType.getType(); + public CriterionSatisfaction addSatisfaction( + CriterionWithItsType criterionWithItsType, Interval interval) { Criterion criterion = criterionWithItsType.getCriterion(); - if (canBeActivated(criterionWithItsType, start, finish)) { - CriterionSatisfaction newSatisfaction = new CriterionSatisfaction( - start, criterion, this); - criterionSatisfactions.add(newSatisfaction); + ICriterionType type = criterionWithItsType.getType(); + CriterionSatisfaction newSatisfaction = createNewSatisfaction(interval, + criterion); + if (canAddSatisfaction(criterionWithItsType, interval)) { + Date finish = getFinishDate(type, newSatisfaction, interval); if (finish != null) { newSatisfaction.finish(finish); } - if (!type.allowMultipleActiveCriterionsPerResource()) { - for (CriterionSatisfaction criterionSatisfaction : getActiveSatisfactionsAt( - type, start)) { - if (newSatisfaction != criterionSatisfaction) { - criterionSatisfaction.finish(start); - } - } - Set posterior = getSatisfactionsPosteriorTo( - type, newSatisfaction); - Date earliest = getEarliestStartDate(posterior); - if (earliest != null) { - newSatisfaction.finish(earliest); - } - } + criterionSatisfactions.add(newSatisfaction); + return newSatisfaction; } else { throw new IllegalStateException( "this resource is activaved for other criterion of the same type"); } - } - private static Date getEarliestStartDate( - Set posterior) { - Date earliest = null; - for (CriterionSatisfaction criterionSatisfaction : posterior) { - if (earliest == null) { - earliest = criterionSatisfaction.getStartDate(); + private CriterionSatisfaction createNewSatisfaction(Interval interval, + Criterion criterion) { + CriterionSatisfaction newSatisfaction = new CriterionSatisfaction( + criterion, this, interval); + return newSatisfaction; + } + + private Date getFinishDate(ICriterionType type, + CriterionSatisfaction newSatisfaction, Interval interval) { + if (!type.allowSimultaneousCriterionsPerResource()) { + CriterionSatisfaction posterior = getNext(type, newSatisfaction); + if (posterior != null && posterior.overlapsWith(interval)) { + assert !posterior.overlapsWith(Interval.range(interval + .getStart(), posterior.getStartDate())); + return posterior.getStartDate(); } - earliest = Collections.min(Arrays.asList(earliest, - criterionSatisfaction.getStartDate())); } - return earliest; + return interval.getEnd(); } - private Set getSatisfactionsPosteriorTo( - ICriterionType type, CriterionSatisfaction newSatisfaction) { - Date start = newSatisfaction.getStartDate(); - Date finish = newSatisfaction.isFinished() ? newSatisfaction.getEndDate() : null; - Set posterior = new HashSet(); - if (finish != null) { - posterior.addAll(getActiveSatisfactionsAt(type, finish)); + /** + * @param orderedSatisfactions + * @param newSatisfaction + * @return the position in which if newSatisfaction is inserted would comply + * with the following: + *
    + *
  • newSatisfaction startDate would be equal or posterior to all + * the previous satisfactions
  • + *
  • newSatisfaction startDate would be previous to all the + * posterior satisfactions
  • + *
+ */ + private int findPlace(List orderedSatisfactions, + CriterionSatisfaction newSatisfaction) { + int position = Collections.binarySearch(orderedSatisfactions, + newSatisfaction, CriterionSatisfaction.BY_START_COMPARATOR); + if (position >= 0) { + return position + 1; } else { - ArrayList result = new ArrayList(); - for (CriterionSatisfaction satisfaction : getSatisfactionsFor(type)) { - if (!satisfaction.isFinished() && satisfaction.getStartDate().after(start)) { - result.add(satisfaction); - } - } - posterior.addAll(result); - } - posterior.remove(newSatisfaction); - return posterior; - } - - public void deactivate(CriterionWithItsType criterionWithItsType) { - for (CriterionSatisfaction criterionSatisfaction : getActiveSatisfactionsFor(criterionWithItsType.getCriterion())) { - criterionSatisfaction.finish(new Date()); + return Math.abs(position) - 1; } } - private boolean noneOf(CriterionWithItsType criterionWithItsType, - Date start, Date end) { + public List finish( + CriterionWithItsType criterionWithItsType) { + return finishEnforcedAt(criterionWithItsType, new Date()); + } + + public List finishEnforcedAt( + CriterionWithItsType criterionWithItsType, Date date) { + ArrayList result = new ArrayList(); + for (CriterionSatisfaction criterionSatisfaction : query().from( + criterionWithItsType.getType()).at(date).result()) { + criterionSatisfaction.finish(date); + result.add(criterionSatisfaction); + } + return result; + } + + public boolean canAddSatisfaction( + CriterionWithItsType criterionWithItsType, Interval interval) { ICriterionType type = criterionWithItsType.getType(); - Criterion criterion = criterionWithItsType.getCriterion(); - return getActiveSatisfactionsForIn(type, start, end).size() == getActiveSatisfactionsForIn( - criterion, start, end).size(); + if (!type.criterionCanBeRelatedTo(getClass())) { + return false; + } + if (type.allowSimultaneousCriterionsPerResource()) { + return true; + } + CriterionSatisfaction newSatisfaction = createNewSatisfaction(interval, + criterionWithItsType.getCriterion()); + CriterionSatisfaction previous = getPrevious(criterionWithItsType + .getType(), newSatisfaction); + return previous == null || !previous.overlapsWith(interval); } - public boolean canBeActivated(CriterionWithItsType criterionWithItsType) { - return canBeActivated(criterionWithItsType, new Date()); + public boolean canAddSatisfaction(ICriterionType type, + CriterionSatisfaction satisfaction) { + return new EnsureSatisfactionIsCorrect(this, type, satisfaction) + .canAddSatisfaction(); } - public boolean canBeActivated(CriterionWithItsType criterionWithItsType, - Date start) { - return canBeActivated(criterionWithItsType, start, null); + private CriterionSatisfaction getNext(ICriterionType type, + CriterionSatisfaction newSatisfaction) { + List ordered = query().from(type) + .sortByStartDate().result(); + int position = findPlace(ordered, newSatisfaction); + CriterionSatisfaction next = position != ordered.size() ? ordered + .get(position) : null; + return next; } - public boolean canBeActivated(CriterionWithItsType criterionWithItsType, - Date start, Date finish) { - ICriterionType type = criterionWithItsType.getType(); - return type.criterionCanBeRelatedTo(getClass()) && (type.allowMultipleActiveCriterionsPerResource() || noneOf( - criterionWithItsType, start, finish)); + private CriterionSatisfaction getPrevious(ICriterionType type, + CriterionSatisfaction newSatisfaction) { + List ordered = query().from(type) + .sortByStartDate().result(); + int position = findPlace(ordered, newSatisfaction); + CriterionSatisfaction previous = position > 0 ? ordered + .get(position - 1) : null; + return previous; } public void removeCriterionSatisfaction(CriterionSatisfaction satisfaction) @@ -271,4 +376,8 @@ public abstract class Resource { criterionSatisfactions.remove(satisfaction); } + public boolean contains(CriterionSatisfaction satisfaction) { + return criterionSatisfactions.contains(satisfaction); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java index a0e0f8794..e812f74f2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/CriterionServiceImpl.java @@ -14,6 +14,7 @@ import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.ICriterion; import org.navalplanner.business.resources.entities.ICriterionOnData; import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.services.CriterionService; import org.navalplanner.business.resources.services.ResourceService; @@ -100,7 +101,7 @@ public class CriterionServiceImpl implements CriterionService { ICriterionType criterionType) { ArrayList result = new ArrayList(); for (Resource resource : resourceService.getResources()) { - result.addAll(resource.getActiveSatisfactionsFor(criterionType)); + result.addAll(resource.getCurrentSatisfactionsFor(criterionType)); } return result; } @@ -110,8 +111,8 @@ public class CriterionServiceImpl implements CriterionService { ICriterionType criterionType, Date start, Date end) { ArrayList result = new ArrayList(); for (Resource resource : resourceService.getResources()) { - result.addAll(resource.getActiveSatisfactionsForIn(criterionType, - start, end)); + result.addAll(resource.query().from(criterionType).enforcedInAll( + Interval.range(start, end)).result()); } return result; } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/CriterionSatisfactionTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/CriterionSatisfactionTest.java index 65200e3dc..53096ce29 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/CriterionSatisfactionTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/CriterionSatisfactionTest.java @@ -1,17 +1,23 @@ package org.navalplanner.business.test.resources.entities; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; import org.junit.Test; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionSatisfaction; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.test.resources.daos.CriterionDAOTest; -import org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest; import static junit.framework.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest.year; /** * Tests for {@link CriterionSatisfaction}
@@ -24,8 +30,8 @@ public class CriterionSatisfactionTest { final Criterion criterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); CriterionSatisfaction criterionSatisfaction = new CriterionSatisfaction( - CriterionSatisfactionDAOTest.year(2000), criterion, worker); - Date end = CriterionSatisfactionDAOTest.year(2006); + year(2000), criterion, worker); + Date end = year(2006); criterionSatisfaction.finish(end); assertTrue(criterionSatisfaction.isFinished()); assertEquals(end, criterionSatisfaction.getEndDate()); @@ -38,7 +44,7 @@ public class CriterionSatisfactionTest { public void canFinishWhenItStarted() throws Exception { final Criterion criterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); - Date start = CriterionSatisfactionDAOTest.year(2000); + Date start = year(2000); CriterionSatisfaction criterionSatisfaction = new CriterionSatisfaction( start, criterion, worker); criterionSatisfaction.finish(start); @@ -50,29 +56,75 @@ public class CriterionSatisfactionTest { final Criterion criterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); CriterionSatisfaction criterionSatisfaction = new CriterionSatisfaction( - CriterionSatisfactionDAOTest.year(2000), criterion, worker); - criterionSatisfaction.finish(CriterionSatisfactionDAOTest.year(1999)); + year(2000), criterion, worker); + criterionSatisfaction.finish(year(1999)); } @Test - public void isValidAtDate() { + public void testIsEnforcedAtDate() { final Criterion criterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); CriterionSatisfaction criterionSatisfaction = new CriterionSatisfaction( - CriterionSatisfactionDAOTest.year(2000), criterion, worker); - assertTrue(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(3000))); - assertTrue(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(2000))); - assertFalse(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(1999))); - criterionSatisfaction.finish(CriterionSatisfactionDAOTest.year(2005)); - assertFalse(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(3000))); - assertTrue(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(2000))); - assertFalse(criterionSatisfaction - .isActiveAt(CriterionSatisfactionDAOTest.year(1999))); + year(2000), criterion, worker); + assertTrue(criterionSatisfaction.isEnforcedAt(year(3000))); + assertTrue(criterionSatisfaction.isEnforcedAt(year(2000))); + assertFalse(criterionSatisfaction.isEnforcedAt(year(1999))); + criterionSatisfaction.finish(year(2005)); + assertFalse(criterionSatisfaction.isEnforcedAt(year(3000))); + assertFalse(criterionSatisfaction.isAlwaysEnforcedIn(Interval.range( + year(2001), year(2006)))); + assertTrue(criterionSatisfaction.isEnforcedAt(year(2000))); + assertTrue(criterionSatisfaction.isEnforcedAt(year(2001))); + assertFalse(criterionSatisfaction.isEnforcedAt(year(1999))); } + @Test + public void testEnforcedAtSomePointInInterval() { + final Criterion criterion = CriterionDAOTest.createValidCriterion(); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + CriterionSatisfaction criterionSatisfaction = new CriterionSatisfaction( + year(2000), criterion, worker); + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(2001), year(4000)))); + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(2005), year(4000)))); + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(1999), year(2001)))); + + criterionSatisfaction.finish(year(2004)); + + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(2002), year(4000)))); + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(2002), null))); + assertFalse(criterionSatisfaction.overlapsWith(Interval.range( + year(2005), year(4000)))); + assertTrue(criterionSatisfaction.overlapsWith(Interval.range( + year(1999), null))); + assertFalse(criterionSatisfaction.overlapsWith(Interval.range( + year(1990), year(1995)))); + } + + @Test + public void testCriterionSatisfactionsStartComparator() { + final Criterion criterion = CriterionDAOTest.createValidCriterion(); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + Interval[] intervals = { Interval.from(year(1000)), + Interval.range(year(1100), year(9000)), + Interval.point(year(1101)), Interval.from(year(1200)), + Interval.range(year(3000), year(4000)) }; + List orderedSatisfactions = new ArrayList(); + for (Interval interval : intervals) { + orderedSatisfactions.add(new CriterionSatisfaction(criterion, + worker, interval)); + } + List copy = new ArrayList( + orderedSatisfactions); + assertThat(copy, equalTo(orderedSatisfactions)); + for (int i = 0; i < 20; i++) { + Collections.shuffle(copy); + Collections.sort(copy, CriterionSatisfaction.BY_START_COMPARATOR); + assertThat(copy, equalTo(orderedSatisfactions)); + } + } } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/IntervalTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/IntervalTest.java new file mode 100644 index 000000000..80c351c56 --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/IntervalTest.java @@ -0,0 +1,149 @@ +package org.navalplanner.business.test.resources.entities; + +import java.util.Date; + +import org.junit.Test; +import org.navalplanner.business.resources.entities.Interval; +import org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest.year; + +public class IntervalTest { + + @Test(expected = IllegalArgumentException.class) + public void testStartDateMustBeBeforeThanEndDate() throws Exception { + Interval.range(CriterionSatisfactionDAOTest.year(2000), + CriterionSatisfactionDAOTest.year(1999)); + } + + @Test(expected = IllegalArgumentException.class) + public void testStartDateMustBeNotNull() throws Exception { + Interval.range(null, CriterionSatisfactionDAOTest.year(1999)); + } + + @Test + public void intervalCanBeOpenEnded() throws Exception { + Interval.range(CriterionSatisfactionDAOTest.year(1990), null); + } + + @Test + public void testContainsPointInTime() { + Interval openEnded = Interval.from(CriterionSatisfactionDAOTest + .year(1990)); + Interval range = Interval.range( + CriterionSatisfactionDAOTest.year(1990), + CriterionSatisfactionDAOTest.year(2000)); + Interval startPoint = Interval.point(year(1990)); + Interval endPoint = Interval.point(year(2000)); + assertTrue(openEnded.contains(CriterionSatisfactionDAOTest.year(5000))); + assertFalse(range.contains(year(5000))); + assertTrue(range.contains(year(1990))); + assertFalse(range.contains(year(2000))); + assertTrue(range.contains(year(1991))); + assertFalse(range.contains(year(1989))); + assertFalse(range.includes(startPoint)); + assertFalse(range.includes(endPoint)); + } + + @Test + public void testPointsOnlyContainsThemselves() throws Exception { + Interval point = Interval + .point(CriterionSatisfactionDAOTest.year(1990)); + assertTrue(point.contains(CriterionSatisfactionDAOTest.year(1990))); + assertFalse(point.contains(CriterionSatisfactionDAOTest.year(2010))); + } + + @Test + public void testIntervalsAreStartInclusiveAndEndExclusive() + throws Exception { + Interval range = Interval.range(year(1990), year(2000)); + assertTrue(range.contains(year(1990))); + assertFalse(range.contains(year(2000))); + assertFalse(range.contains(new Date(year(1990).getTime() - 1))); + assertFalse(range.contains(new Date(year(1990).getTime() - 1))); + assertFalse(range.contains(new Date(year(2000).getTime() + 1))); + } + + @Test + public void testIncludes() throws Exception { + Interval bigRange = Interval.range(year(1990), year(2000)); + Interval included = Interval.range(year(1990), year(1998)); + Interval point = Interval.point(year(1996)); + assertTrue(bigRange.includes(included)); + assertTrue(bigRange.includes(point)); + assertTrue(included.includes(point)); + } + + @Test + public void testStartPointDoesntOverlapsWithRange() throws Exception { + Interval range = Interval.range(year(1990), year(2005)); + Interval openEndedRange = Interval.from(year(1990)); + Interval point = Interval.point(year(1990)); + Interval internalPoint = Interval.point(year(1991)); + + assertFalse(point.overlapsWith(range)); + assertFalse(point.overlapsWith(openEndedRange)); + assertFalse(range.overlapsWith(point)); + assertFalse(openEndedRange.overlapsWith(point)); + + assertTrue(internalPoint.overlapsWith(range)); + assertTrue(internalPoint.overlapsWith(openEndedRange)); + assertTrue(range.overlapsWith(internalPoint)); + assertTrue(openEndedRange.overlapsWith(internalPoint)); + } + + @Test + public void testOverlapsWith() { + Interval firstRange = Interval.range(year(1990), year(2005)); + Interval igalia = Interval.from(year(2001)); + Interval distantPoint = Interval.point(year(2030)); + Interval pointInFirstRange = Interval.point(year(2000)); + Interval outRange = Interval.range(year(2020), year(2030)); + assertTrue(firstRange.overlapsWith(igalia)); + assertTrue(firstRange.overlapsWith(Interval.range(year(1980), + year(1991)))); + assertTrue(igalia.overlapsWith(firstRange)); + assertTrue(outRange.overlapsWith(igalia)); + assertFalse(outRange.overlapsWith(firstRange)); + assertTrue(distantPoint.overlapsWith(igalia)); + assertFalse(distantPoint.overlapsWith(firstRange)); + assertTrue(igalia.overlapsWith(distantPoint)); + assertTrue(distantPoint.overlapsWith(igalia)); + assertFalse(firstRange.overlapsWith(distantPoint)); + assertTrue(firstRange.overlapsWith(pointInFirstRange)); + } + + @Test + public void testIntervalFinishingAtTheStartOfOtherDontOverlap() + throws Exception { + Interval range = Interval.range(year(2000), year(2005)); + Interval from = Interval.from(year(2000)); + Interval before = Interval.range(year(1995), year(2000)); + assertFalse(range.overlapsWith(before)); + assertFalse(before.overlapsWith(range)); + + assertFalse(from.overlapsWith(before)); + assertFalse(before.overlapsWith(from)); + } + + @Test + public void testStartPointDoesntOverlapsWithRanges() { + Interval range = Interval.range(year(2010), year(2030)); + Interval point = Interval.point(year(2010)); + Interval otherRange = Interval.from(year(2010)); + assertFalse(point.overlapsWith(range)); + assertFalse(range.overlapsWith(point)); + assertFalse(point.overlapsWith(otherRange)); + assertFalse(otherRange.overlapsWith(point)); + } + + @Test + public void testCreatingPointWithRange() throws Exception { + Interval point = Interval.point(year(1990)); + Interval range = Interval.range(year(1990), year(1990)); + assertEquals(point, range); + } +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java index 4351ebac3..d562b126e 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/entities/ResourceTest.java @@ -1,7 +1,10 @@ package org.navalplanner.business.test.resources.entities; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashSet; +import java.util.List; import org.junit.Test; import org.navalplanner.business.resources.entities.Criterion; @@ -10,6 +13,7 @@ import org.navalplanner.business.resources.entities.CriterionTypeBase; import org.navalplanner.business.resources.entities.CriterionWithItsType; import org.navalplanner.business.resources.entities.ICriterion; import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.test.resources.daos.CriterionDAOTest; @@ -21,6 +25,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.navalplanner.business.test.resources.daos.CriterionSatisfactionDAOTest.year; /** * Tests for {@link Resource}.
@@ -36,10 +41,10 @@ public class ResourceTest { CriterionTypeBase otherType = createTypeThatMatches(false, other); Worker worker = new Worker("firstName", "surName", "2333232", 10); assertThat(worker.getSatisfactionsFor(criterion).size(), equalTo(0)); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); assertTrue(criterion.isSatisfiedBy(worker)); assertThat(worker.getSatisfactionsFor(criterion).size(), equalTo(1)); - worker.activate(new CriterionWithItsType(otherType, other)); + worker.addSatisfaction(new CriterionWithItsType(otherType, other)); assertTrue(other.isSatisfiedBy(worker)); assertThat(worker.getSatisfactionsFor(other).size(), equalTo(1)); assertThat(worker.getSatisfactionsFor(criterion).size(), equalTo(1)); @@ -48,17 +53,16 @@ public class ResourceTest { @Test(expected = IllegalArgumentException.class) public void getSatisfactionsForWrongIntervalThrowsException() { Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.getActiveSatisfactionsForIn(CriterionDAOTest - .createValidCriterion(), CriterionSatisfactionDAOTest - .year(2000), CriterionSatisfactionDAOTest.year(1999)); + worker.query().from(CriterionDAOTest.createValidCriterion()) + .enforcedInAll(Interval.range(year(2000), year(1999))) + .result(); } @Test(expected = IllegalArgumentException.class) public void getSatisfactionsForWrongIntervalForCriterionTypeThrowsException() { Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.getActiveSatisfactionsForIn(createTypeThatMatches(), - CriterionSatisfactionDAOTest.year(2000), - CriterionSatisfactionDAOTest.year(1999)); + worker.query().from(createTypeThatMatches()).enforcedInAll( + Interval.range(year(2000), year(1999))).current().result(); } @Test @@ -71,14 +75,14 @@ public class ResourceTest { CriterionWithItsType otherCriterionWithItsType = new CriterionWithItsType( createTypeThatMatches(otherCriterion), otherCriterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - worker.activate(otherCriterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(4000)); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); + worker.addSatisfaction(otherCriterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(4000))); assertEquals(2, worker.getSatisfactionsFor(criterionType).size()); - assertEquals(1, worker.getActiveSatisfactionsFor(criterionType).size()); + assertEquals(1, worker.getCurrentSatisfactionsFor(criterionType).size()); } @Test @@ -92,16 +96,16 @@ public class ResourceTest { CriterionWithItsType otherCriterionWithItsType = new CriterionWithItsType( type, otherCriterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(0)); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(1)); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2002)); - assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(1)); - worker.activate(otherCriterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - assertThat(worker.getActiveCriterionsFor(type).size(), equalTo(2)); + assertThat(worker.getCurrentCriterionsFor(type).size(), equalTo(0)); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); + assertThat(worker.getCurrentCriterionsFor(type).size(), equalTo(1)); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2002))); + assertThat(worker.getCurrentCriterionsFor(type).size(), equalTo(1)); + worker.addSatisfaction(otherCriterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); + assertThat(worker.getCurrentCriterionsFor(type).size(), equalTo(2)); } public static CriterionTypeBase createTypeThatMatches( @@ -152,23 +156,23 @@ public class ResourceTest { createTypeThatMatches(otherCriterion), otherCriterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2003)); - worker.activate(otherCriterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); + worker.addSatisfaction(criterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2003))); + worker.addSatisfaction(otherCriterionWithItsType, Interval + .from(CriterionSatisfactionDAOTest.year(2000))); assertEquals(2, worker.getSatisfactionsFor(criterionType).size()); - assertEquals(1, worker.getActiveSatisfactionsForIn(criterionType, - CriterionSatisfactionDAOTest.year(2001), - CriterionSatisfactionDAOTest.year(2005)).size()); - assertEquals(2, worker.getActiveSatisfactionsForIn(criterionType, - CriterionSatisfactionDAOTest.year(2004), - CriterionSatisfactionDAOTest.year(2005)).size()); - assertEquals(0, worker.getActiveSatisfactionsForIn(criterionType, - CriterionSatisfactionDAOTest.year(1999), - CriterionSatisfactionDAOTest.year(2005)).size()); + assertEquals(1, worker.query().from(criterionType).enforcedInAll( + Interval.range(year(2001), year(2005))).current().result() + .size()); + assertEquals(2, worker.query().from(criterionType).enforcedInAll( + Interval.range(year(2004), year(2005))).current().result() + .size()); + assertEquals(0, worker.query().from(criterionType).enforcedInAll( + Interval.range(year(1999), year(2005))).current().result() + .size()); } @Test(expected = IllegalArgumentException.class) @@ -180,114 +184,184 @@ public class ResourceTest { } @Test - public void testActivateAndDeactivateCriterion() { + public void testAddAndRemoveSatisfactions() { Criterion criterion = CriterionDAOTest.createValidCriterion(); Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); ICriterionType criterionType = createTypeThatMatches(false, criterion, otherCriterion); - assertThat(worker.getActiveSatisfactionsFor(criterion).size(), + assertThat(worker.getCurrentSatisfactionsFor(criterion).size(), equalTo(0)); assertFalse(criterion.isSatisfiedBy(worker)); - assertTrue(worker.canBeActivated(new CriterionWithItsType( - criterionType, criterion))); - worker.activate(new CriterionWithItsType(criterionType, criterion)); + Interval fromNow = Interval.from(new Date()); + assertTrue(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, criterion), fromNow)); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), fromNow); assertTrue(criterion.isSatisfiedBy(worker)); - assertThat(worker.getActiveSatisfactionsFor(criterion).size(), + assertThat(worker.getCurrentSatisfactionsFor(criterion).size(), equalTo(1)); - assertFalse(worker.canBeActivated(new CriterionWithItsType( - criterionType, otherCriterion))); + assertFalse(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, otherCriterion), fromNow)); try { - worker.activate(new CriterionWithItsType(criterionType, + worker.addSatisfaction(new CriterionWithItsType(criterionType, otherCriterion)); fail("must send exception since it already is activated for a criterion of the same type and the type doesn't allow repeated criterions per resource"); } catch (IllegalStateException e) { // ok } - assertTrue(worker.canBeActivated(new CriterionWithItsType( - criterionType, criterion))); - assertThat(worker.getActiveSatisfactionsFor(criterion).size(), - equalTo(1)); - worker.deactivate(new CriterionWithItsType(criterionType, criterion)); - assertTrue("the satisfactions are deactivated", worker - .getActiveSatisfactionsFor(criterion).isEmpty()); + assertThat(worker.query().from(criterionType).enforcedInAll(fromNow) + .result().size(), equalTo(1)); + List finished = worker.finishEnforcedAt( + new CriterionWithItsType(criterionType, criterion), fromNow + .getStart()); + assertThat(finished.size(), equalTo(1)); + assertTrue("all satisfactions are finished", worker.query().from( + criterionType).enforcedInAll(fromNow).result().isEmpty()); + assertTrue(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, criterion), fromNow)); } @Test - public void testActivateInDate() throws Exception { + public void testAddAtDate() throws Exception { Criterion criterion = CriterionDAOTest.createValidCriterion(); Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); ICriterionType criterionType = createTypeThatMatches(false, criterion, otherCriterion); - worker.activate(new CriterionWithItsType(criterionType, criterion), - CriterionSatisfactionDAOTest.year(4000)); - worker.activate(new CriterionWithItsType(criterionType, criterion), - CriterionSatisfactionDAOTest.year(5000), - CriterionSatisfactionDAOTest.year(6000)); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), Interval.range(year(5000), year(6000))); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), Interval.from(year(4000))); - assertThat(worker.getActiveSatisfactionsForIn(criterion, - CriterionSatisfactionDAOTest.year(4001), - CriterionSatisfactionDAOTest.year(4999)).size(), equalTo(1)); - assertThat(worker.getActiveSatisfactionsForIn(criterion, - CriterionSatisfactionDAOTest.year(5001), - CriterionSatisfactionDAOTest.year(5500)).size(), equalTo(1)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(4001), year(4999))).result().size(), + equalTo(1)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(4001), year(5000))).result().size(), + equalTo(0)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(5000), year(5001))).result().size(), + equalTo(1)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(5001), year(5500))).result().size(), + equalTo(1)); - worker.deactivate(new CriterionWithItsType(criterionType, criterion)); + worker.finish(new CriterionWithItsType(criterionType, criterion)); - assertThat(worker.getActiveSatisfactionsForIn(criterion, - CriterionSatisfactionDAOTest.year(4001), - CriterionSatisfactionDAOTest.year(4999)).size(), equalTo(1)); - assertThat(worker.getActiveSatisfactionsForIn(criterion, - CriterionSatisfactionDAOTest.year(5001), - CriterionSatisfactionDAOTest.year(5500)).size(), equalTo(1)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(4001), year(4999))).result().size(), + equalTo(1)); + assertThat(worker.query().from(criterion).enforcedInAll( + Interval.range(year(5001), year(5500))).result().size(), + equalTo(1)); - assertFalse(worker.canBeActivated(new CriterionWithItsType( - criterionType, otherCriterion), CriterionSatisfactionDAOTest - .year(4001))); + assertFalse(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, otherCriterion), Interval + .from(CriterionSatisfactionDAOTest.year(4001)))); } @Test // when type doesnt allow multiple active criterions per resource - public void activateOnlyUntilNextCriterionIsActive() { + public void addOnlyUntilNextCriterionIsActive() { Criterion criterion = CriterionDAOTest.createValidCriterion(); Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); ICriterionType criterionType = createTypeThatMatches(false, criterion, otherCriterion); - worker.activate(new CriterionWithItsType(criterionType, criterion), - CriterionSatisfactionDAOTest.year(4000)); - worker.activate( - new CriterionWithItsType(criterionType, otherCriterion), - CriterionSatisfactionDAOTest.year(3500)); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), Interval.from(year(4000))); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + otherCriterion), Interval.from(year(3500))); assertThat(worker.getSatisfactionsFor(otherCriterion).size(), equalTo(1)); CriterionSatisfaction satisfaction = worker.getSatisfactionsFor( otherCriterion).iterator().next(); - assertThat(satisfaction.getEndDate(), - equalTo(CriterionSatisfactionDAOTest.year(4000))); + assertThat(satisfaction.getEndDate(), equalTo(year(4000))); } @Test(expected = IllegalStateException.class) // when type doesnt allow multiple active criterions per resource - public void deactivatePrevious() { + public void testCantAddOverlappingTotally() { Criterion criterion = CriterionDAOTest.createValidCriterion(); Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); ICriterionType criterionType = createTypeThatMatches(false, criterion, otherCriterion); - worker.activate( - new CriterionWithItsType(criterionType, otherCriterion), - CriterionSatisfactionDAOTest.year(3500)); - assertFalse(worker.canBeActivated(new CriterionWithItsType( - criterionType, criterion), CriterionSatisfactionDAOTest - .year(4000))); - worker.activate(new CriterionWithItsType(criterionType, criterion), - CriterionSatisfactionDAOTest.year(4000)); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + otherCriterion), Interval.from(year(3500))); + assertFalse(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, criterion), Interval.range(year(4000), + year(5000)))); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), Interval.range(year(4000), year(5000))); } @Test(expected = IllegalStateException.class) - public void shouldntActivate() { + public void testCantAddIfOverlapsPartially() { + Criterion criterion = CriterionDAOTest.createValidCriterion(); + Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + ICriterionType criterionType = createTypeThatMatches(false, + criterion, otherCriterion); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + otherCriterion), Interval.range(year(3500), year(4500))); + assertFalse(worker.canAddSatisfaction(new CriterionWithItsType( + criterionType, criterion), Interval.range(year(3600), + year(3800)))); + worker.addSatisfaction(new CriterionWithItsType(criterionType, + criterion), Interval.range(year(3600), year(3800))); + } + + @Test + public void testCantAddWrongCriterionSatisfaction() { + Criterion criterion = CriterionDAOTest.createValidCriterion(); + Criterion otherCriterion = CriterionDAOTest.createValidCriterion(); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + Worker other = new Worker("other", "surName", "2333232", 10); + ICriterionType criterionType = createTypeThatMatches(false, + criterion); + List wrongSatisfactions = new ArrayList(); + CriterionSatisfaction satisfaction = createValid(criterion, worker); + satisfaction.setResource(other); + wrongSatisfactions.add(satisfaction); + satisfaction = createValid(criterion, worker); + satisfaction.setResource(null); + wrongSatisfactions.add(satisfaction); + satisfaction = createValid(criterion, worker); + satisfaction.setCriterion(otherCriterion); + wrongSatisfactions.add(satisfaction); + for (CriterionSatisfaction wrong : wrongSatisfactions) { + try { + worker.addSatisfaction(criterionType, wrong); + fail("must send exception"); + } catch (IllegalArgumentException e) { + // ok + } + } + } + + public void testAddCriterionSatisfaction() throws Exception { + Criterion criterion = CriterionDAOTest.createValidCriterion(); + Worker worker = new Worker("firstName", "surName", "2333232", 10); + ICriterionType criterionType = createTypeThatMatches(false, + criterion); + CriterionSatisfaction satisfaction = createValid(criterion, worker); + worker.addSatisfaction(criterionType, satisfaction); + assertThat(worker.getAllSatisfactions().size(), equalTo(1)); + } + + private CriterionSatisfaction createValid(Criterion criterion, Worker worker) { + CriterionSatisfaction satisfaction = new CriterionSatisfaction(); + satisfaction.setResource(worker); + satisfaction.setStartDate(year(2000)); + satisfaction.setCriterion(criterion); + satisfaction.setEndDate(year(2004)); + return satisfaction; + } + + @Test(expected = IllegalStateException.class) + public void shouldntAdd() { Criterion criterion = CriterionDAOTest.createValidCriterion(); Worker worker = new Worker("firstName", "surName", "2333232", 10); ICriterionType type = new CriterionTypeBase("prueba", false, false, @@ -316,8 +390,9 @@ public class ResourceTest { }; CriterionWithItsType criterionWithItsType = new CriterionWithItsType( type, criterion); - assertFalse(worker.canBeActivated(criterionWithItsType)); - worker.activate(criterionWithItsType); + assertFalse(worker.canAddSatisfaction(criterionWithItsType, Interval + .from(new Date()))); + worker.addSatisfaction(criterionWithItsType); } } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java index bee04a9b6..dc2f18222 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/CriterionServiceTest.java @@ -15,6 +15,7 @@ import org.navalplanner.business.resources.entities.CriterionWithItsType; import org.navalplanner.business.resources.entities.ICriterion; import org.navalplanner.business.resources.entities.ICriterionOnData; import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.PredefinedCriterionTypes; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; @@ -126,7 +127,7 @@ public class CriterionServiceTest { Criterion criterion = CriterionDAOTest.createValidCriterion(); ICriterionType type = createTypeThatMatches(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); assertTrue(criterion.isSatisfiedBy(worker)); resourceService.saveResource(worker); assertTrue(criterion.isSatisfiedBy(worker)); @@ -141,7 +142,7 @@ public class CriterionServiceTest { criterionService.save(criterion); ICriterionType type = createTypeThatMatches(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); assertTrue(criterion.isSatisfiedBy(worker)); resourceService.saveResource(worker); assertTrue(criterion.isSatisfiedBy(worker)); @@ -155,7 +156,7 @@ public class CriterionServiceTest { ICriterionType type = createTypeThatMatches(criterion); criterionService.save(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); resourceService.saveResource(worker); assertThat(criterionService.getResourcesSatisfying(criterion).size(), equalTo(1)); @@ -170,7 +171,7 @@ public class CriterionServiceTest { ICriterionType type = createTypeThatMatches(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); assertEquals(1, criterionService.getResourcesSatisfying(criterion) .size()); } @@ -191,7 +192,7 @@ public class CriterionServiceTest { ICriterionType type = createTypeThatMatches(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(new CriterionWithItsType(type, criterion)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion)); assertThat(criterionService.getResourcesSatisfying(Resource.class, criterion).size(), is(1)); @@ -208,8 +209,7 @@ public class CriterionServiceTest { criterionService.save(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(new CriterionWithItsType(type, criterion), - CriterionSatisfactionDAOTest.year(2000)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion), Interval.from(CriterionSatisfactionDAOTest.year(2000))); ICriterionOnData criterionOnData = criterionService.empower(criterion); assertTrue(criterionOnData.isSatisfiedBy(worker)); assertEquals(1, criterionOnData.getResourcesSatisfying().size()); @@ -232,7 +232,7 @@ public class CriterionServiceTest { Criterion criterion = CriterionDAOTest.createValidCriterion(); criterionService.save(criterion); ICriterionType type = createTypeThatMatches(criterion); - worker1.activate(new CriterionWithItsType(type, criterion)); + worker1.addSatisfaction(new CriterionWithItsType(type, criterion)); resourceService.saveResource(worker1); Resource workerReloaded = criterionService .onTransaction(new OnTransaction() { @@ -266,8 +266,7 @@ public class CriterionServiceTest { criterionService.save(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(new CriterionWithItsType(type, criterion), - CriterionSatisfactionDAOTest.year(2000)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion), Interval.from(CriterionSatisfactionDAOTest.year(2000))); ICriterionOnData criterionOnData = criterionService.empower(criterion); criterionOnData.getResourcesSatisfying(); criterionOnData.getResourcesSatisfying(CriterionSatisfactionDAOTest @@ -292,8 +291,8 @@ public class CriterionServiceTest { type, criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); + worker.addSatisfaction(criterionWithItsType, Interval.from(CriterionSatisfactionDAOTest + .year(2000))); assertEquals(1, criterionService.getResourcesSatisfying(criterion, CriterionSatisfactionDAOTest.year(2001), @@ -302,8 +301,7 @@ public class CriterionServiceTest { CriterionSatisfactionDAOTest.year(1999), CriterionSatisfactionDAOTest.year(2005)).size()); - worker.activate(new CriterionWithItsType(type, criterion), - CriterionSatisfactionDAOTest.year(1998)); + worker.addSatisfaction(new CriterionWithItsType(type, criterion), Interval.from(CriterionSatisfactionDAOTest.year(1998))); assertEquals(1, criterionService.getResourcesSatisfying(criterion, CriterionSatisfactionDAOTest.year(1999), @@ -319,10 +317,10 @@ public class CriterionServiceTest { criterionService.save(criterion); Worker worker = new Worker("firstName", "surName", "2333232", 10); resourceService.saveResource(worker); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(2000)); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(1998)); + worker.addSatisfaction(criterionWithItsType, Interval.from(CriterionSatisfactionDAOTest + .year(2000))); + worker.addSatisfaction(criterionWithItsType, Interval.from(CriterionSatisfactionDAOTest + .year(1998))); ICriterionType criterionType = ResourceTest .createTypeThatMatches(criterion); @@ -337,8 +335,8 @@ public class CriterionServiceTest { CriterionSatisfactionDAOTest.year(1997), CriterionSatisfactionDAOTest.year(2005)).size()); - worker.activate(criterionWithItsType, CriterionSatisfactionDAOTest - .year(1997)); + worker.addSatisfaction(criterionWithItsType, Interval.from(CriterionSatisfactionDAOTest + .year(1997))); assertEquals(2, criterionService.getSatisfactionsFor(criterionType, CriterionSatisfactionDAOTest.year(1999), CriterionSatisfactionDAOTest.year(2005)).size()); @@ -363,18 +361,18 @@ public class CriterionServiceTest { } public static ICriterionType createTypeThatMatches( - final boolean allowMultipleActiveCriterionsPerResource, + final boolean allowSimultaneousCriterionsPerResource, final Criterion criterion) { return new ICriterionType() { @Override - public boolean allowHierarchy() { - return false; + public boolean allowSimultaneousCriterionsPerResource() { + return allowSimultaneousCriterionsPerResource; } @Override - public boolean allowMultipleActiveCriterionsPerResource() { - return allowMultipleActiveCriterionsPerResource; + public boolean allowHierarchy() { + return false; } @Override diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java index 1e9e387b1..d221968b3 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java @@ -138,7 +138,7 @@ public class ResourceServiceTest { criterionService.save(criterion); ICriterionType type = CriterionServiceTest .createTypeThatMatches(criterion); - worker1.activate(new CriterionWithItsType(type, criterion)); + worker1.addSatisfaction(new CriterionWithItsType(type, criterion)); resourceService.saveResource(worker1); assertThat(worker1.getVersion(), not(equalTo(versionValueAfterSave))); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/criterion/CriterionsModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/criterion/CriterionsModel.java index dc755b2c6..43fab7e9b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/criterion/CriterionsModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/criterion/CriterionsModel.java @@ -126,7 +126,7 @@ public class CriterionsModel implements ICriterionsModel { @Override public boolean isChangeAssignmentsDisabled() { return criterionType == null - || !criterionType.allowMultipleActiveCriterionsPerResource(); + || !criterionType.allowSimultaneousCriterionsPerResource(); } @Override @@ -135,7 +135,7 @@ public class CriterionsModel implements ICriterionsModel { for (Resource resource : resources) { Resource reloaded = find(resource.getId()); reloaded - .activate(new CriterionWithItsType(criterionType, criterion)); + .addSatisfaction(new CriterionWithItsType(criterionType, criterion)); resourceService.saveResource(reloaded); } } @@ -145,7 +145,7 @@ public class CriterionsModel implements ICriterionsModel { public void deactivateAll(Collection resources) { for (Resource resource : resources) { Resource reloaded = find(resource.getId()); - reloaded.deactivate(new CriterionWithItsType(criterionType, + reloaded.finish(new CriterionWithItsType(criterionType, criterion)); resourceService.saveResource(reloaded); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java index a1b7f8f40..94bf88f2e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkRelationshipsController.java @@ -1,26 +1,22 @@ package org.navalplanner.web.resources.worker; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.CriterionWithItsType; import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.Worker; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.util.GenericForwardComposer; -import org.zkoss.zul.Datebox; import org.zkoss.zul.Listbox; /** @@ -33,7 +29,10 @@ public class WorkRelationshipsController extends GenericForwardComposer { private WorkerCRUDController workerCRUDController; -/* private CriterionSatisfaction newRelationship = new CriterionSatisfaction(); */ + /* + * private CriterionSatisfaction newRelationship = new + * CriterionSatisfaction(); + */ private CriterionSatisfaction editRelationship = new CriterionSatisfaction(); @@ -41,21 +40,26 @@ public class WorkRelationshipsController extends GenericForwardComposer { private Listbox selectedWorkCriterion; -/* private Datebox newWorkRelationshipStartDate; - - private Datebox newWorkRelationshipEndDate; */ + /* + * private Datebox newWorkRelationshipStartDate; + * + * private Datebox newWorkRelationshipEndDate; + */ private HashMap fromCriterionToType; + private boolean editing; + public WorkRelationshipsController(IWorkerModel workerModel, WorkerCRUDController workerCRUDController) { this.workerModel = workerModel; this.workerCRUDController = workerCRUDController; this.workCriterions = new ArrayList(); - Map, Collection> map = - workerModel.getLaboralRelatedCriterions(); + Map, Collection> map = workerModel + .getLaboralRelatedCriterions(); this.fromCriterionToType = new HashMap(); - for (Entry, Collection> entry : map.entrySet()) { + for (Entry, Collection> entry : map + .entrySet()) { this.workCriterions.addAll(entry.getValue()); for (Criterion criterion : entry.getValue()) { this.fromCriterionToType.put(criterion, @@ -68,66 +72,53 @@ public class WorkRelationshipsController extends GenericForwardComposer { if (this.workerCRUDController.getWorker() == null) { return new HashSet(); } else { - return workerModel.getLaboralRelatedCriterionSatisfactions( - this.workerCRUDController.getWorker()); + return workerModel + .getLaboralRelatedCriterionSatisfactions(this.workerCRUDController + .getWorker()); } } public void deleteCriterionSatisfaction(CriterionSatisfaction satisfaction) throws InstanceNotFoundException { - workerCRUDController.getWorker().removeCriterionSatisfaction(satisfaction); + workerCRUDController.getWorker().removeCriterionSatisfaction( + satisfaction); this.workerCRUDController.goToEditForm(); } - public void setEditCriterionSatisfaction(CriterionSatisfaction crit) { - // the component should be preselected. - this.editRelationship = crit; + public void prepareForCreate() { + this.editRelationship = new CriterionSatisfaction(); + editing = false; + } + + public void prepareForEdit(CriterionSatisfaction criterionSatisfaction) { + this.editRelationship = criterionSatisfaction; + editing = true; } public void saveCriterionSatisfaction() throws InstanceNotFoundException { // Add new criterion - Criterion selectedCriterion = (Criterion) selectedWorkCriterion.getSelectedItem().getValue(); - CriterionWithItsType criterionWithItsType = fromCriterionToType.get(selectedCriterion); - System.out.println( "SAVE!!: " + selectedCriterion.getName() ); - - if (editRelationship.getStartDate() == null) { - this.workerCRUDController.getWorker().activate( - criterionWithItsType, - editRelationship.getStartDate()); - } else { - this.workerCRUDController.getWorker().activate( - criterionWithItsType, - editRelationship.getStartDate(), - editRelationship.getEndDate()); + Criterion selectedCriterion = (Criterion) selectedWorkCriterion + .getSelectedItem().getValue(); + CriterionWithItsType criterionWithItsType = fromCriterionToType + .get(selectedCriterion); + System.out.println("SAVE!!: " + selectedCriterion.getName()); + if (this.workerCRUDController.getWorker().contains(editRelationship)) { + this.workerCRUDController.getWorker().removeCriterionSatisfaction( + editRelationship); } + this.workerCRUDController.getWorker().addSatisfaction( + criterionWithItsType, + Interval.range(editRelationship.getStartDate(), + editRelationship.getEndDate())); // Delete the former one - workerCRUDController.getWorker(). - removeCriterionSatisfaction(this.editRelationship); + workerCRUDController.getWorker().removeCriterionSatisfaction( + this.editRelationship); this.workerCRUDController.goToEditForm(); } - public void addCriterionSatisfaction() { - Criterion selectedCriterion = (Criterion) selectedWorkCriterion.getSelectedItem().getValue(); - CriterionWithItsType criterionWithItsType = fromCriterionToType.get(selectedCriterion); - // never accessed: Unnecesary as edition does this. - System.out.println( "SELECTED: " + criterionWithItsType.toString() ); - - if (editRelationship.getStartDate() == null) { - this.workerCRUDController.getWorker().activate( - criterionWithItsType, - editRelationship.getStartDate()); - } else { - this.workerCRUDController.getWorker().activate( - criterionWithItsType, - editRelationship.getStartDate(), - editRelationship.getEndDate()); - } - this.workerCRUDController.goToEditForm(); - } - @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); @@ -142,4 +133,8 @@ public class WorkRelationshipsController extends GenericForwardComposer { return this.workCriterions; } + public boolean isEditing() { + return editing; + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java index bc230877a..cafd97c5c 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerCRUDController.java @@ -1,17 +1,11 @@ package org.navalplanner.web.resources.worker; -import java.util.Date; import java.util.List; import org.hibernate.validator.InvalidValue; import org.navalplanner.business.common.exceptions.ValidationException; -import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionSatisfaction; -import org.navalplanner.business.resources.entities.CriterionTypeBase; -import org.navalplanner.business.resources.entities.CriterionWithItsType; -import org.navalplanner.business.resources.entities.PredefinedCriterionTypes; import org.navalplanner.business.resources.entities.Worker; -import org.navalplanner.business.resources.entities.WorkingRelationship; import org.navalplanner.business.resources.services.CriterionService; import org.navalplanner.web.common.IMessagesForUser; import org.navalplanner.web.common.Level; @@ -53,7 +47,7 @@ public class WorkerCRUDController extends GenericForwardComposer implements private Component messagesContainer; - private WorkRelationshipsController workRelationship; + private WorkRelationshipsController addWorkRelationship; private LocalizationsController localizationsForEditionController; @@ -61,13 +55,16 @@ public class WorkerCRUDController extends GenericForwardComposer implements private CriterionService criterionService; + private WorkRelationshipsController editWorkRelationship; + public WorkerCRUDController() { } public WorkerCRUDController(Window createWindow, Window listWindow, Window editWindow, Window workRelationshipsWindow, - Window addWorkRelationshipWindow, Window editWorkRelationshipWindow, - IWorkerModel workerModel, IMessagesForUser messages) { + Window addWorkRelationshipWindow, + Window editWorkRelationshipWindow, IWorkerModel workerModel, + IMessagesForUser messages) { this.createWindow = createWindow; this.listWindow = listWindow; this.editWindow = editWindow; @@ -131,24 +128,9 @@ public class WorkerCRUDController extends GenericForwardComposer implements } public void goToAddWorkRelationshipForm() { - Criterion selectedCriterion = criterionService.load( - WorkingRelationship.HIRED.criterion()); - CriterionWithItsType criteriontype = new CriterionWithItsType( - PredefinedCriterionTypes.WORK_RELATIONSHIP, selectedCriterion); - this.workerModel.getWorker().activate(criteriontype,new Date()); - /* CriterionSatisfaction newSatisfaction = - new CriterionSatisfaction( - new Date(), - selectedCriterion, - this.workerModel.getWorker()); */ - CriterionSatisfaction newSatisfaction = - this.workerModel.getWorker(). - getActiveSatisfactionsFor(selectedCriterion).iterator().next(); - this.workRelationship.setEditCriterionSatisfaction(newSatisfaction); + this.addWorkRelationship.prepareForCreate(); getVisibility().showOnly(addWorkRelationshipWindow); Util.reloadBindings(addWorkRelationshipWindow); -// getVisibility().showOnly(editWorkRelationshipWindow); -// Util.reloadBindings(editWorkRelationshipWindow); } public void goToCreateForm() { @@ -158,7 +140,7 @@ public class WorkerCRUDController extends GenericForwardComposer implements } public void goToEditWorkRelationshipForm(CriterionSatisfaction satisfaction) { - this.workRelationship.setEditCriterionSatisfaction(satisfaction); + this.editWorkRelationship.prepareForEdit(satisfaction); getVisibility().showOnly(editWorkRelationshipWindow); Util.reloadBindings(editWorkRelationshipWindow); } @@ -175,15 +157,27 @@ public class WorkerCRUDController extends GenericForwardComposer implements if (messagesContainer == null) throw new RuntimeException("messagesContainer is needed"); messages = new MessagesForUser(messagesContainer); - this.workRelationship = - new WorkRelationshipsController(this.workerModel,this); - this.workRelationship.doAfterCompose( - comp.getFellow("addWorkRelationshipWindow")); - URLHandler handler = URLHandlerRegistry + this.addWorkRelationship = new WorkRelationshipsController( + this.workerModel, this); + setupWorkRelationshipController(this.addWorkRelationship, + this.addWorkRelationshipWindow); + setupWorkRelationshipController( + this.editWorkRelationship = new WorkRelationshipsController( + this.workerModel, this), editWorkRelationshipWindow); + + URLHandler handler = URLHandlerRegistry .getRedirectorFor(IWorkerCRUDControllerEntryPoints.class); handler.applyIfMatches(this); } + private void setupWorkRelationshipController( + WorkRelationshipsController workRelationshipController, + Window workRelationshipWindow) throws Exception { + workRelationshipController.doAfterCompose(workRelationshipWindow); + workRelationshipWindow.setVariable("workRelationship", + workRelationshipController, true); + } + private LocalizationsController createLocalizationsController( Component comp, String localizationsContainerName) throws Exception { LocalizationsController localizationsController = new LocalizationsController( @@ -198,13 +192,13 @@ public class WorkerCRUDController extends GenericForwardComposer implements if (visibility == null) { visibility = new OnlyOneVisible(listWindow, editWindow, createWindow, workRelationshipsWindow, - addWorkRelationshipWindow, editWorkRelationshipWindow ); + addWorkRelationshipWindow, editWorkRelationshipWindow); } return visibility; } public GenericForwardComposer getWorkRelationship() { - return this.workRelationship; + return this.addWorkRelationship; } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java index 01dce1624..15b2565a3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/worker/WorkerModel.java @@ -19,6 +19,7 @@ import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.CriterionWithItsType; import org.navalplanner.business.resources.entities.ICriterionType; +import org.navalplanner.business.resources.entities.Interval; import org.navalplanner.business.resources.entities.PredefinedCriterionTypes; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; @@ -165,7 +166,7 @@ public class WorkerModel implements IWorkerModel { ICriterionType type) { Validate .isTrue( - type.allowMultipleActiveCriterionsPerResource(), + type.allowSimultaneousCriterionsPerResource(), "must allow multiple active criterions for this type to use this assignment strategy"); this.criterionService = criterionService; this.resource = resource; @@ -197,13 +198,13 @@ public class WorkerModel implements IWorkerModel { private HashSet calculateInitialActive() { return new HashSet(resource - .getActiveSatisfactionsFor(type)); + .getCurrentSatisfactionsFor(type)); } private List calculateInitialCriterionsNotAssigned() { Map allCriterions = byId(criterionService .getCriterionsFor(type)); - for (Long activeId : asIds(resource.getActiveCriterionsFor(type))) { + for (Long activeId : asIds(resource.getCurrentCriterionsFor(type))) { allCriterions.remove(activeId); } return new ArrayList(allCriterions.values()); @@ -250,12 +251,11 @@ public class WorkerModel implements IWorkerModel { @Override public void applyChanges() { for (CriterionSatisfaction criterionSatisfaction : added) { - resource.activate(new CriterionWithItsType(type, - criterionSatisfaction.getCriterion()), - criterionSatisfaction.getStartDate()); + resource.addSatisfaction(new CriterionWithItsType(type, + criterionSatisfaction.getCriterion()), Interval.from(criterionSatisfaction.getStartDate())); } for (Criterion criterion : unassigned.keySet()) { - resource.deactivate(new CriterionWithItsType(type, criterion)); + resource.finish(new CriterionWithItsType(type, criterion)); } } } diff --git a/navalplanner-webapp/src/main/webapp/resources/worker/_editWorkRelationship.zul b/navalplanner-webapp/src/main/webapp/resources/worker/_editWorkRelationship.zul index 71bfa62d4..276405568 100644 --- a/navalplanner-webapp/src/main/webapp/resources/worker/_editWorkRelationship.zul +++ b/navalplanner-webapp/src/main/webapp/resources/worker/_editWorkRelationship.zul @@ -1,5 +1,4 @@ - + @@ -9,11 +8,11 @@ + value="@{workRelationship.editRelationship.startDate}" /> + value="@{workRelationship.editRelationship.endDate}" /> + model="@{workRelationship.workCriterions}" disabled="@{! workRelationship.editing}"> @@ -21,7 +20,7 @@ -