org.libreplan.business.planner.entities.SpecificResourceAllocation.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.business.planner.entities.SpecificResourceAllocation.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.libreplan.business.planner.entities;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.Validate;
import javax.validation.constraints.NotNull;
import javax.validation.Valid;
import org.joda.time.LocalDate;
import org.libreplan.business.calendars.entities.AvailabilityTimeLine;
import org.libreplan.business.calendars.entities.AvailabilityTimeLine.FixedPoint;
import org.libreplan.business.calendars.entities.AvailabilityTimeLine.Interval;
import org.libreplan.business.calendars.entities.Capacity;
import org.libreplan.business.calendars.entities.CombinedWorkHours;
import org.libreplan.business.calendars.entities.ICalendar;
import org.libreplan.business.common.ProportionalDistributor;
import org.libreplan.business.planner.entities.allocationalgorithms.ResourcesPerDayModification;
import org.libreplan.business.planner.limiting.entities.LimitingResourceQueueElement;
import org.libreplan.business.resources.daos.IResourcesSearcher;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.util.deepcopy.OnCopy;
import org.libreplan.business.util.deepcopy.Strategy;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workingday.EffortDuration.IEffortFrom;
import org.libreplan.business.workingday.IntraDayDate;
import org.libreplan.business.workingday.IntraDayDate.PartialDay;
import org.libreplan.business.workingday.ResourcesPerDay;

/**
 * Represents the relation between {@link Task} and a specific {@link Worker}.
 *
 * @author Manuel Rego Casasnovas <mrego@igalia.com>
 */
public class SpecificResourceAllocation extends ResourceAllocation<SpecificDayAssignment> implements IAllocatable {

    @OnCopy(Strategy.SHARE)
    private Resource resource;

    private Set<SpecificDayAssignmentsContainer> specificDayAssignmentsContainers = new HashSet<>();

    public SpecificResourceAllocation() {
    }

    public static SpecificResourceAllocation create(Task task) {
        return create(new SpecificResourceAllocation(task));
    }

    /**
     * Creates a {@link SpecificResourceAllocation} for a {@link LimitingResourceQueueElement}.
     *
     * The process of creating a specific resource allocation for a queue
     * element is different as it's necessary to assign a resource and a number
     * of resources per day without allocating day assignments.
     *
     * @param resource
     * @param task
     * @return
     */
    public static SpecificResourceAllocation createForLimiting(Resource resource, Task task) {
        assert resource.isLimitingResource();
        SpecificResourceAllocation result = create(new SpecificResourceAllocation(task));
        result.setResource(resource);
        result.setResourcesPerDayToAmount(1);

        return result;
    }

    @Valid
    public Set<SpecificDayAssignmentsContainer> getSpecificDayAssignmentsContainers() {
        return new HashSet<>(specificDayAssignmentsContainers);
    }

    public static SpecificResourceAllocation createForTesting(ResourcesPerDay resourcesPerDay, Task task) {
        return create(new SpecificResourceAllocation(resourcesPerDay, task));
    }

    @Override
    protected SpecificDayAssignmentsContainer retrieveOrCreateContainerFor(Scenario scenario) {
        SpecificDayAssignmentsContainer retrieved = retrieveContainerFor(scenario);
        if (retrieved != null) {
            return retrieved;
        }
        SpecificDayAssignmentsContainer result = SpecificDayAssignmentsContainer.create(this, scenario);
        specificDayAssignmentsContainers.add(result);

        return result;
    }

    @Override
    protected SpecificDayAssignmentsContainer retrieveContainerFor(Scenario scenario) {
        return containersByScenario().get(scenario);
    }

    private SpecificResourceAllocation(ResourcesPerDay resourcesPerDay, Task task) {
        super(resourcesPerDay, task);
    }

    private SpecificResourceAllocation(Task task) {
        super(task);
    }

    @NotNull
    public Resource getResource() {
        return resource;
    }

    private Map<Scenario, SpecificDayAssignmentsContainer> containersByScenario() {
        Map<Scenario, SpecificDayAssignmentsContainer> result = new HashMap<>();
        for (SpecificDayAssignmentsContainer each : specificDayAssignmentsContainers) {
            assert !result.containsKey(each);
            result.put(each.getScenario(), each);
        }
        return result;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }

    @Override
    public void allocate(ResourcesPerDay resourcesPerDay) {
        Validate.notNull(resourcesPerDay);
        Validate.notNull(resource);
        new SpecificAssignmentsAllocator().allocate(resourcesPerDay);
    }

    @Override
    public IAllocateResourcesPerDay resourcesPerDayUntil(IntraDayDate endExclusive) {
        return new SpecificAssignmentsAllocator().resourcesPerDayUntil(endExclusive);
    }

    @Override
    public IAllocateResourcesPerDay resourcesPerDayFromEndUntil(IntraDayDate start) {
        return new SpecificAssignmentsAllocator().resourcesPerDayFromEndUntil(start);
    }

    @Override
    public IAllocateEffortOnInterval fromStartUntil(IntraDayDate end) {
        return new SpecificAssignmentsAllocator().fromStartUntil(end);
    }

    @Override
    public IAllocateEffortOnInterval fromEndUntil(IntraDayDate start) {
        return new SpecificAssignmentsAllocator().fromEndUntil(start);
    }

    private final class SpecificAssignmentsAllocator extends AssignmentsAllocator {

        @Override
        public List<SpecificDayAssignment> distributeForDay(PartialDay day, EffortDuration effort) {
            return Collections.singletonList(SpecificDayAssignment.create(day.getDate(), effort, resource));
        }

        @Override
        protected AvailabilityTimeLine getResourcesAvailability() {
            return AvailabilityCalculator.getCalendarAvailabilityFor(resource);
        }

        @Override
        protected Capacity getCapacityAt(PartialDay day) {
            return day.limitCapacity(getAllocationCalendar().getCapacityWithOvertime(day.getDate()));
        }

    }

    public IEffortDistributor<SpecificDayAssignment> createEffortDistributor() {
        return new SpecificAssignmentsAllocator();
    }

    @Override
    public IAllocateEffortOnInterval onIntervalWithinTask(LocalDate start, LocalDate end) {
        return new SpecificAssignmentsAllocator().onIntervalWithinTask(start, end);
    }

    @Override
    public IAllocateEffortOnInterval onIntervalWithinTask(IntraDayDate start, IntraDayDate end) {
        return new SpecificAssignmentsAllocator().onIntervalWithinTask(start, end);
    }

    @Override
    public IAllocateEffortOnInterval onInterval(LocalDate startInclusive, LocalDate endExclusive) {
        return new SpecificAssignmentsAllocator().onInterval(startInclusive, endExclusive);
    }

    @Override
    public IAllocateEffortOnInterval onInterval(IntraDayDate start, IntraDayDate end) {
        return new SpecificAssignmentsAllocator().onInterval(start, end);
    }

    @Override
    protected ICalendar getCalendarGivenTaskCalendar(ICalendar taskCalendar) {
        return CombinedWorkHours.minOf(taskCalendar, getResource().getCalendar());
    }

    @Override
    protected Class<SpecificDayAssignment> getDayAssignmentType() {
        return SpecificDayAssignment.class;
    }

    @Override
    public IAllocatable withPreviousAssociatedResources() {
        return this;
    }

    @Override
    public List<Resource> getAssociatedResources() {
        return Collections.singletonList(resource);
    }

    @Override
    ResourceAllocation<SpecificDayAssignment> createCopy(Scenario scenario) {
        SpecificResourceAllocation result = create(getTask());
        result.resource = getResource();

        return result;
    }

    @Override
    public ResourcesPerDayModification withDesiredResourcesPerDay(ResourcesPerDay resourcesPerDay) {
        return ResourcesPerDayModification.create(this, resourcesPerDay);
    }

    @Override
    public List<Resource> querySuitableResources(IResourcesSearcher resourcesSearcher) {
        return Collections.singletonList(resource);
    }

    @Override
    protected void setItselfAsParentFor(SpecificDayAssignment dayAssignment) {
        dayAssignment.setSpecificResourceAllocation(this);
    }

    @Override
    public void makeAssignmentsContainersDontPoseAsTransientAnyMore() {
        for (SpecificDayAssignmentsContainer each : specificDayAssignmentsContainers) {
            each.dontPoseAsTransientObjectAnymore();
        }
    }

    @Override
    public void copyAssignments(Scenario from, Scenario to) {
        SpecificDayAssignmentsContainer fromContainer = retrieveOrCreateContainerFor(from);
        SpecificDayAssignmentsContainer toContainer = retrieveOrCreateContainerFor(to);
        toContainer.resetTo(fromContainer.getDayAssignments());
    }

    @Override
    protected void removePredecessorContainersFor(Scenario scenario) {
        Map<Scenario, SpecificDayAssignmentsContainer> byScenario = containersByScenario();
        for (Scenario each : scenario.getPredecessors()) {
            SpecificDayAssignmentsContainer container = byScenario.get(each);
            if (container != null) {
                specificDayAssignmentsContainers.remove(container);
            }
        }
    }

    @Override
    protected void removeContainersFor(Scenario scenario) {
        SpecificDayAssignmentsContainer container = containersByScenario().get(scenario);
        if (container != null) {
            specificDayAssignmentsContainers.remove(container);
        }
    }

    /**
     * It does an allocation using the provided {@link EffortDuration} in the
     * not consolidated part in interval from the first day not consolidated to the end provided.
     * All previous not consolidated assignments are removed.
     *
     * @param effortForNotConsolidatedPart
     * @param endExclusive
     */
    public void allocateWholeAllocationKeepingProportions(EffortDuration effortForNotConsolidatedPart,
            IntraDayDate end) {

        AllocationInterval interval = new AllocationInterval(getIntraDayStartDate(), end);

        List<DayAssignment> nonConsolidatedAssignments = interval.getNoConsolidatedAssignmentsOnInterval();
        ProportionalDistributor distributor = ProportionalDistributor.create(asSeconds(nonConsolidatedAssignments));

        EffortDuration[] effortsPerDay = asEfforts(
                distributor.distribute(effortForNotConsolidatedPart.getSeconds()));
        allocateTheWholeAllocation(interval, assignmentsForEfforts(nonConsolidatedAssignments, effortsPerDay));
    }

    private EffortDuration[] asEfforts(int[] secondsArray) {
        EffortDuration[] result = new EffortDuration[secondsArray.length];

        for (int i = 0; i < result.length; i++) {
            result[i] = EffortDuration.seconds(secondsArray[i]);
        }
        return result;
    }

    private List<SpecificDayAssignment> assignmentsForEfforts(List<DayAssignment> assignments,
            EffortDuration[] newEffortsPerDay) {

        List<SpecificDayAssignment> result = new ArrayList<>();
        int i = 0;
        for (DayAssignment each : assignments) {
            EffortDuration durationForAssignment = newEffortsPerDay[i++];
            result.add(SpecificDayAssignment.create(each.getDay(), durationForAssignment, resource));
        }
        return result;
    }

    private int[] asSeconds(List<DayAssignment> assignments) {
        int[] result = new int[assignments.size()];
        int i = 0;

        for (DayAssignment each : assignments) {
            result[i++] = each.getDuration().getSeconds();
        }
        return result;
    }

    public void overrideConsolidatedDayAssignments(SpecificResourceAllocation origin) {
        if (origin != null) {
            List<SpecificDayAssignment> originAssignments = origin.getConsolidatedAssignments();
            resetAssignmentsTo(SpecificDayAssignment.copyToAssignmentsWithoutParent(originAssignments));
        }
    }

    @Override
    public EffortDuration getAssignedEffort(Criterion criterion, final IntraDayDate startInclusive,
            final IntraDayDate endExclusive) {

        return EffortDuration.sum(
                getIntervalsRelatedWith(criterion, startInclusive.getDate(), endExclusive.asExclusiveEnd()),
                new IEffortFrom<Interval>() {
                    @Override
                    public EffortDuration from(Interval each) {
                        FixedPoint intervalStart = (FixedPoint) each.getStart();
                        FixedPoint intervalEnd = (FixedPoint) each.getEnd();

                        return getAssignedDuration(IntraDayDate.convert(intervalStart.getDate(), startInclusive),
                                IntraDayDate.convert(intervalEnd.getDate(), endExclusive));
                    }
                });
    }

    private List<Interval> getIntervalsRelatedWith(Criterion criterion, LocalDate startInclusive,
            LocalDate endExclusive) {

        Interval queryInterval = AvailabilityTimeLine.Interval.create(startInclusive, endExclusive);

        List<Interval> result = new ArrayList<>();

        for (Interval each : getIntervalsThisAllocationInterferesWith(criterion)) {
            if (queryInterval.overlaps(each)) {
                result.add(queryInterval.intersect(each));
            }
        }
        return result;
    }

    private List<Interval> getIntervalsThisAllocationInterferesWith(Criterion criterion) {

        AvailabilityTimeLine availability = AvailabilityCalculator
                .getCriterionsAvailabilityFor(Collections.singleton(criterion), resource);

        availability.invalidUntil(getStartDate());
        availability.invalidFrom(getEndDate());

        return availability.getValidPeriods();
    }

    public boolean interferesWith(Criterion criterion, LocalDate startInclusive, LocalDate endExclusive) {
        return !getIntervalsRelatedWith(criterion, startInclusive, endExclusive).isEmpty();
    }

}