diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java index f079bd469..b274ffcee 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.ResourceAllocation; @@ -57,29 +58,30 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { } public LocalDate untilAllocating(int hoursToAllocate) { + LocalDate start = LocalDate.fromDateFields(task.getStartDate()); int i = 0; int maxDaysElapsed = 0; - for (Integer each : hoursForEachAllocation(hoursToAllocate)) { - ResourcesPerDayModification currentAllocation = allocations.get(i); - int daysElapsedForCurrent = untilAllocating(currentAllocation, each); + for (HoursPerAllocation each : hoursPerAllocation(start, + hoursToAllocate)) { + int daysElapsedForCurrent = untilAllocating(start, each.allocation, + each.hours); maxDaysElapsed = Math.max(maxDaysElapsed, daysElapsedForCurrent); i++; } setAssignmentsForEachAllocation(); - LocalDate start = LocalDate.fromDateFields(task.getStartDate()); return start.plusDays(maxDaysElapsed); } - private int untilAllocating( + private List hoursPerAllocation(LocalDate start, + int toBeAssigned) { + return new HoursPerAllocationCalculator(allocations) + .calculateHoursPerAllocation(start, toBeAssigned); + } + + private int untilAllocating(LocalDate start, ResourcesPerDayModification resourcesPerDayModification, Integer hoursToAllocate) { int hoursRemaining = hoursToAllocate; - LocalDate start = new LocalDate(task.getStartDate().getTime()); - if (!thereAreAvailableHoursFrom(start, resourcesPerDayModification, - hoursToAllocate)) { - markUnsatisfied(resourcesPerDayModification.getBeingModified()); - return 0; - } int day = 0; while (hoursRemaining > 0) { LocalDate current = start.plusDays(day); @@ -126,65 +128,130 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { return DayAssignment.sum(newAssignments); } - private List hoursForEachAllocation(int toBeAssigned) { - BigDecimal[] limits = new BigDecimal[allocations.size()]; - BigDecimal sumAll = sumAll(); - for (int i = 0; i < limits.length; i++) { - BigDecimal amount = allocations.get(i).getGoal() - .getAmount(); - limits[i] = amount.divide(sumAll, RoundingMode.DOWN).multiply( - new BigDecimal(toBeAssigned)); - } - final int remainder = toBeAssigned - sumIntegerParts(limits); - return distributeRemainder(limits, remainder); - } + private static class HoursPerAllocation { + final int hours; - private List distributeRemainder(BigDecimal[] decimals, - final int remainder) { - for (int i = 0; i < remainder; i++) { - int position = positionOfBiggestDecimalPart(decimals); - decimals[position] = new BigDecimal( - decimals[position].intValue() + 1); - } - return asIntegers(decimals); - } + final ResourcesPerDayModification allocation; - private List asIntegers(BigDecimal[] decimals) { - Integer[] result = new Integer[decimals.length]; - for (int i = 0; i < result.length; i++) { - result[i] = decimals[i].intValue(); + private HoursPerAllocation(int hours, + ResourcesPerDayModification allocation) { + this.hours = hours; + this.allocation = allocation; } - return Arrays.asList(result); - } - private int positionOfBiggestDecimalPart(BigDecimal[] decimals) { - int result = 0; - BigDecimal currentBiggestDecimalPart = new BigDecimal(0); - for (int i = 0; i < decimals.length; i++) { - BigDecimal fractionalPart = decimals[i].subtract(new BigDecimal( - decimals[i].intValue())); - if (currentBiggestDecimalPart.compareTo(fractionalPart) < 0) { - currentBiggestDecimalPart = fractionalPart; - result = i; + public static List wrap( + List allocations, + List hours) { + Validate.isTrue(hours.size() == allocations.size()); + int i = 0; + List result = new ArrayList(); + for(i = 0; i < allocations.size(); i++){ + result.add(new HoursPerAllocation(hours.get(i), allocations + .get(i))); } + return result; } - return result; } - private int sumIntegerParts(BigDecimal[] decimals) { - int sum = 0; - for (BigDecimal decimal : decimals) { - sum += decimal.intValue(); - } - return sum; - } + private class HoursPerAllocationCalculator { + private List allocations; - private BigDecimal sumAll() { - BigDecimal result = new BigDecimal(0); - for (ResourcesPerDayModification r : allocations) { - result = result.add(r.getGoal().getAmount()); + private HoursPerAllocationCalculator( + List allocations) { + this.allocations = new ArrayList( + allocations); } - return result; + + public List calculateHoursPerAllocation( + LocalDate start, int toAssign) { + do { + List hours = calculateHours(toAssign); + List result = HoursPerAllocation.wrap( + allocations, hours); + List unsatisfied = getUnsatisfied( + start, result); + if (unsatisfied.isEmpty()) { + return result; + } + for (ResourcesPerDayModification each : unsatisfied) { + markUnsatisfied(each.getBeingModified()); + } + allocations.removeAll(unsatisfied); + } while (true); + } + + private List getUnsatisfied( + LocalDate start, List hoursPerAllocations) { + List cannotSatisfy = new ArrayList(); + for (HoursPerAllocation each : hoursPerAllocations) { + if (!thereAreAvailableHoursFrom(start, each.allocation, + each.hours)) { + cannotSatisfy.add(each.allocation); + } + } + return cannotSatisfy; + } + + private List calculateHours(int toAssign) { + BigDecimal[] limits = new BigDecimal[allocations.size()]; + BigDecimal sumAll = sumAll(); + for (int i = 0; i < limits.length; i++) { + BigDecimal amount = allocations.get(i).getGoal().getAmount(); + limits[i] = amount.divide(sumAll, RoundingMode.DOWN).multiply( + new BigDecimal(toAssign)); + } + final int remainder = toAssign - sumIntegerParts(limits); + return distributeRemainder(limits, remainder); + } + + private List distributeRemainder(BigDecimal[] decimals, + final int remainder) { + for (int i = 0; i < remainder; i++) { + int position = positionOfBiggestDecimalPart(decimals); + decimals[position] = new BigDecimal(decimals[position] + .intValue() + 1); + } + return asIntegers(decimals); + } + + private List asIntegers(BigDecimal[] decimals) { + Integer[] result = new Integer[decimals.length]; + for (int i = 0; i < result.length; i++) { + result[i] = decimals[i].intValue(); + } + return Arrays.asList(result); + } + + private int positionOfBiggestDecimalPart(BigDecimal[] decimals) { + int result = 0; + BigDecimal currentBiggestDecimalPart = new BigDecimal(0); + for (int i = 0; i < decimals.length; i++) { + BigDecimal fractionalPart = decimals[i] + .subtract(new BigDecimal(decimals[i].intValue())); + if (currentBiggestDecimalPart.compareTo(fractionalPart) < 0) { + currentBiggestDecimalPart = fractionalPart; + result = i; + } + } + return result; + } + + private int sumIntegerParts(BigDecimal[] decimals) { + int sum = 0; + for (BigDecimal decimal : decimals) { + sum += decimal.intValue(); + } + return sum; + } + + private BigDecimal sumAll() { + BigDecimal result = new BigDecimal(0); + for (ResourcesPerDayModification r : allocations) { + result = result.add(r.getGoal().getAmount()); + } + return result; + } + } }