diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/exceptions/ValidationException.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/exceptions/ValidationException.java index e4d93f229..2c330c19f 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/exceptions/ValidationException.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/exceptions/ValidationException.java @@ -35,6 +35,17 @@ public class ValidationException extends Exception { return invalidValues; } + public ValidationException(InvalidValue invalidValue) { + super(); + storeInvalidValues(toArray(invalidValue)); + } + + private InvalidValue[] toArray(InvalidValue invalidValue) { + InvalidValue[] invalidValues = new InvalidValue[1]; + invalidValues[0] = invalidValue; + return invalidValues; + } + public ValidationException(InvalidValue[] invalidValues) { super(); storeInvalidValues(invalidValues); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java index 0edb5fad0..e98f63590 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java @@ -207,16 +207,17 @@ public class CriterionType extends BaseEntity implements } } + /** + * A {@link CriterionType} can be related with {@link Resource} matching + * attribute resource and it's always related with resource of type RESOURCE + */ @Override public boolean criterionCanBeRelatedTo(Class klass) { - for (ResourceEnum resource : ResourceEnum.values()) { - if (resource.isAssignableFrom(klass)) { - return true; - } - } - return false; + return ResourceEnum.RESOURCE.equals(resource()) + || resource().isAssignableFrom(klass); } + /** * Two criterion types are equals if they both got the same name * 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 6c34cdd1d..27828706a 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 @@ -33,12 +33,14 @@ import java.util.Map; import java.util.Set; import org.apache.commons.lang.Validate; +import org.hibernate.validator.InvalidValue; import org.joda.time.Days; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.IWorkHours; import org.navalplanner.business.calendars.entities.ResourceCalendar; import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay; import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.planner.entities.DayAssignment; // FIXME: Alternatively, Resource can be modeled with the style: @@ -315,10 +317,22 @@ public abstract class Resource extends BaseEntity{ newSatisfaction.validate(); criterionSatisfactions.add(newSatisfaction); return newSatisfaction; - } else { - throw new IllegalStateException(" The "+criterion.getName()+ - " can not be assigned to this resource. Its interval overlap with other criterion"); } + final String message = getReasonForNotAddingSatisfaction(type); + throw new IllegalStateException(message); + } + + private String getReasonForNotAddingSatisfaction(ICriterionType type) { + if (cannotApplyResourceToCriterionType(type)) { + return "Cannot apply criterion of type " + type.getName() + + " to a " + getClass().getSimpleName(); + } else { + return "Criterion satisfaction overlaps with other criterion satisfactions"; + } + } + + private boolean cannotApplyResourceToCriterionType(ICriterionType type) { + return (type != null && !type.criterionCanBeRelatedTo(getClass())); } private CriterionSatisfaction createNewSatisfaction(Interval interval, @@ -401,6 +415,43 @@ public abstract class Resource extends BaseEntity{ public boolean canAddSatisfaction( CriterionWithItsType criterionWithItsType, Interval interval) { + CriterionSatisfaction satisfaction = createNewSatisfaction(interval, criterionWithItsType.getCriterion()); + return canAddSatisfaction(criterionWithItsType.getType(), satisfaction, this.getCriterionSatisfactions()); + } + + private boolean canAddSatisfaction(ICriterionType type, CriterionSatisfaction satisfaction, Set satisfactions) { + final Criterion criterion = satisfaction.getCriterion(); + final Interval interval = Interval.range(satisfaction.getStartDate(), satisfaction.getEndDate()); + + if (!type.criterionCanBeRelatedTo(getClass())) { + return false; + } + + CriterionSatisfaction previousSameCriterion = getPreviousSameCriterion + (criterion, satisfaction, satisfactions); + CriterionSatisfaction posteriorSameCriterion = getNextSameCriterion + (criterion, satisfaction, satisfactions); + + boolean canAdd = ((previousSameCriterion == null || + !previousSameCriterion.overlapsWith(interval)) && + ( posteriorSameCriterion == null || + !posteriorSameCriterion.overlapsWith(interval))); + + if(!canAdd) return false; + if (type.isAllowSimultaneousCriterionsPerResource()){ + return true; + } + + CriterionSatisfaction previous = getPrevious(type , satisfaction, satisfactions); + CriterionSatisfaction posterior = getNext(type, satisfaction, satisfactions); + + return (previous == null || !previous.overlapsWith(interval)) && + ( posterior == null || !posterior.overlapsWith(interval)); + } + + public boolean _canAddSatisfaction( + CriterionWithItsType criterionWithItsType, Interval interval) { + ICriterionType type = criterionWithItsType.getType(); Criterion criterion = criterionWithItsType.getCriterion(); if (!type.criterionCanBeRelatedTo(getClass())) { @@ -623,7 +674,7 @@ public abstract class Resource extends BaseEntity{ return calendar.getTime(); } - public void addSatisfactions(Set addlist){ + public void addSatisfactions(Set addlist) throws ValidationException { //Create a newList with new Satisfactions and the old satisfactions Set newList = new HashSet(addlist); for(CriterionSatisfaction satisfaction : criterionSatisfactions){ @@ -638,48 +689,37 @@ public abstract class Resource extends BaseEntity{ activeList.add(satisfaction); } } - if(isValidSatisfactions(activeList)){ - criterionSatisfactions.clear(); - criterionSatisfactions.addAll(newList); - }else - throw new IllegalStateException( - "This set of satisfaction not is " + - "valid because exists overlap with others criterions satisfaction"); + validateSatisfactions(activeList); + criterionSatisfactions.clear(); + criterionSatisfactions.addAll(newList); } - public boolean isValidSatisfactions(Set newList){ - for(CriterionSatisfaction satisfaction : newList){ - //Copied list without the satisfaction to check - Set checkedList = new HashSet(newList); - checkedList.remove(satisfaction); - - CriterionType type = satisfaction.getCriterion().getType(); - Criterion criterion = satisfaction.getCriterion(); - Interval interval = satisfaction.getInterval(); - - CriterionSatisfaction previousSameCriterion = getPreviousSameCriterion - (criterion, satisfaction,checkedList); - CriterionSatisfaction posteriorSameCriterion = getNextSameCriterion - (criterion, satisfaction,checkedList); - - boolean canAdd = ((previousSameCriterion == null || - !previousSameCriterion.overlapsWith(interval)) && - ( posteriorSameCriterion == null || - !posteriorSameCriterion.overlapsWith(interval))); - - if(!canAdd) return false; - if (type.isAllowSimultaneousCriterionsPerResource()){ - return true; + private void validateSatisfactions(Set satisfactions) + throws ValidationException { + for (CriterionSatisfaction satisfaction : satisfactions) { + final Set remainingSatisfactions = new HashSet(); + remainingSatisfactions.addAll(satisfactions); + remainingSatisfactions.remove(satisfaction); + validateSatisfaction(satisfaction, remainingSatisfactions); } - - CriterionSatisfaction previous = getPrevious(type,satisfaction,checkedList); - CriterionSatisfaction posterior = getNext(type,satisfaction,checkedList); - - return (previous == null || !previous.overlapsWith(interval)) && - ( posterior == null || !posterior.overlapsWith(interval)); - } - return true; } + private void validateSatisfaction(CriterionSatisfaction satisfaction, + Set satisfactions) + throws ValidationException { + + if (!canAddSatisfaction(satisfaction, satisfactions)) { + String message = getReasonForNotAddingSatisfaction(satisfaction + .getCriterion().getType()); + final InvalidValue invalidValue = new InvalidValue(message, + CriterionSatisfaction.class, "resource", this, satisfaction); + throw new ValidationException(invalidValue); + } + } + + private boolean canAddSatisfaction(CriterionSatisfaction satisfaction, Set satisfactions) { + final ICriterionType type = satisfaction.getCriterion().getType(); + return canAddSatisfaction(type, satisfaction, satisfactions); + } }