/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico 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.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
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 org.joda.time.LocalDate;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.Registry;
import org.libreplan.business.resources.entities.MachineWorkersConfigurationUnit;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.util.deepcopy.OnCopy;
import org.libreplan.business.util.deepcopy.Strategy;
/**
* @author Óscar González Fernández <ogonzalez@igalia.com>
*
*/
public class DerivedAllocation extends BaseEntity {
public static Map<MachineWorkersConfigurationUnit, DerivedAllocation> byConfigurationUnit(
Collection<? extends DerivedAllocation> derivedAllocations) {
Map<MachineWorkersConfigurationUnit, DerivedAllocation> map = new HashMap<MachineWorkersConfigurationUnit, DerivedAllocation>();
for (DerivedAllocation each : derivedAllocations) {
map.put(each.getConfigurationUnit(), each);
}
return map;
}
private static boolean isIfGenericContainsMachine(
ResourceAllocation<?> derivedFrom,
MachineWorkersConfigurationUnit configurationUnit) {
if (derivedFrom instanceof GenericResourceAllocation) {
GenericResourceAllocation generic = (GenericResourceAllocation) derivedFrom;
return generic.getAssociatedResources().contains(
configurationUnit.getMachine());
}
return true;
}
private static boolean isIfSpecificSameMachine(
ResourceAllocation<?> derivedFrom,
MachineWorkersConfigurationUnit configurationUnit) {
if (derivedFrom instanceof SpecificResourceAllocation) {
SpecificResourceAllocation specific = (SpecificResourceAllocation) derivedFrom;
return specific.getResource()
.equals(configurationUnit.getMachine());
}
return true;
}
public static DerivedAllocation create(ResourceAllocation<?> derivedFrom,
MachineWorkersConfigurationUnit configurationUnit) {
return create(new DerivedAllocation(derivedFrom, configurationUnit));
}
@NotNull
private ResourceAllocation<?> derivedFrom;
@NotNull
private MachineWorkersConfigurationUnit configurationUnit;
@OnCopy(Strategy.IGNORE)
private Set<DerivedDayAssignmentsContainer> derivedDayAssignmentsContainers = new HashSet<DerivedDayAssignmentsContainer>();
private Map<Scenario, DerivedDayAssignmentsContainer> byScenario() {
Map<Scenario, DerivedDayAssignmentsContainer> result = new HashMap<Scenario, DerivedDayAssignmentsContainer>();
for (DerivedDayAssignmentsContainer each : derivedDayAssignmentsContainers) {
result.put(each.getScenario(), each);
}
return result;
}
private DerivedDayAssignmentsContainer retrieveOrCreate(
Scenario scenario) {
DerivedDayAssignmentsContainer result = byScenario().get(scenario);
if (result == null) {
result = DerivedDayAssignmentsContainer.create(this, scenario);
derivedDayAssignmentsContainers.add(result);
}
return result;
}
@OnCopy(Strategy.IGNORE)
private DayAssignmentsState dayAssignmentsState;
private abstract class DayAssignmentsState {
List<DerivedDayAssignment> getAssignments() {
return DayAssignment.orderedByDay(getUnorderedAssignments());
}
int getHours() {
return DayAssignment.sum(getUnorderedAssignments()).roundToHours();
}
abstract void resetAssignmentsTo(
List<DerivedDayAssignment> dayAssignments);
abstract void resetAssignmentsTo(LocalDate startInclusive,
LocalDate endExclusive,
List<DerivedDayAssignment> newAssignments);
protected abstract Collection<DerivedDayAssignment> getUnorderedAssignments();
abstract DayAssignmentsState useScenario(Scenario scenario);
}
private class NotSpecifiedScenarioState extends DayAssignmentsState {
@Override
protected Collection<DerivedDayAssignment> getUnorderedAssignments() {
Scenario current = Registry.getScenarioManager().getCurrent();
DerivedDayAssignmentsContainer container = byScenario()
.get(current);
if (container == null) {
return new ArrayList<DerivedDayAssignment>();
}
return container.getDayAssignments();
}
@Override
void resetAssignmentsTo(List<DerivedDayAssignment> dayAssignments) {
throwNotModifiable();
}
@Override
void resetAssignmentsTo(LocalDate startInclusive,
LocalDate endExclusive,
List<DerivedDayAssignment> newAssignments) {
throwNotModifiable();
}
private void throwNotModifiable() {
throw new IllegalStateException(
"the scenario has not been specified");
}
@Override
DayAssignmentsState useScenario(Scenario scenario) {
return new SpecifiedScenarioState(scenario);
}
}
private class TransientState extends DayAssignmentsState {
private List<DerivedDayAssignment> assignments = new ArrayList<DerivedDayAssignment>();
@Override
public Collection<DerivedDayAssignment> getUnorderedAssignments() {
return assignments;
}
@Override
void resetAssignmentsTo(List<DerivedDayAssignment> dayAssignments) {
assignments = copyAssignmentsAsChildrenOf(dayAssignments);
}
private List<DerivedDayAssignment> copyAssignmentsAsChildrenOf(
List<DerivedDayAssignment> dayAssignments) {
List<DerivedDayAssignment> result = new ArrayList<DerivedDayAssignment>();
for (DerivedDayAssignment each : dayAssignments) {
result.add(each.copyAsChildOf(DerivedAllocation.this));
}
return result;
}
@Override
void resetAssignmentsTo(LocalDate startInclusive,
LocalDate endExclusive,
List<DerivedDayAssignment> newAssignments) {
checkAreValid(newAssignments);
List<DerivedDayAssignment> toBeRemoved = DayAssignment
.getAtInterval(getAssignments(), startInclusive,
endExclusive);
assignments.removeAll(toBeRemoved);
detachAssignments(toBeRemoved);
assignments.addAll(DayAssignment.getAtInterval(newAssignments,
startInclusive, endExclusive));
}
@Override
DayAssignmentsState useScenario(Scenario scenario) {
return new SpecifiedScenarioState(scenario, assignments);
}
}
private class SpecifiedScenarioState extends DayAssignmentsState {
private final Scenario scenario;
private SpecifiedScenarioState(Scenario scenario) {
Validate.notNull(scenario);
this.scenario = scenario;
}
public SpecifiedScenarioState(Scenario scenario,
List<DerivedDayAssignment> assignments) {
this(scenario);
resetAssignmentsTo(assignments);
}
@Override
protected Collection<DerivedDayAssignment> getUnorderedAssignments() {
DerivedDayAssignmentsContainer container = retrieveOrCreate(scenario);
return container.getDayAssignments();
}
@Override
void resetAssignmentsTo(List<DerivedDayAssignment> dayAssignments) {
DerivedDayAssignmentsContainer container = retrieveOrCreate(scenario);
container.resetAssignmentsTo(dayAssignments);
}
@Override
void resetAssignmentsTo(LocalDate startInclusive,
LocalDate endExclusive,
List<DerivedDayAssignment> newAssignments) {
DerivedDayAssignmentsContainer container = retrieveOrCreate(scenario);
container.resetAssignmentsTo(startInclusive, endExclusive,
newAssignments);
}
@Override
DayAssignmentsState useScenario(Scenario scenario) {
return new SpecifiedScenarioState(scenario);
}
}
public BigDecimal getAlpha() {
return configurationUnit.getAlpha();
}
public List<Resource> getResources() {
Set<Resource> result = DayAssignment
.getAllResources(dayAssignmentsState.getUnorderedAssignments());
return new ArrayList<Resource>(result);
}
public int getHours() {
return dayAssignmentsState.getHours();
}
public String getName() {
return configurationUnit.getName();
}
/**
* Constructor for hibernate. DO NOT USE!
*/
public DerivedAllocation() {
this.dayAssignmentsState = new NotSpecifiedScenarioState();
}
public DerivedAllocation(ResourceAllocation<?> derivedFrom,
MachineWorkersConfigurationUnit configurationUnit) {
Validate.notNull(derivedFrom);
Validate.notNull(configurationUnit);
Validate
.isTrue(isIfSpecificSameMachine(derivedFrom, configurationUnit));
Validate.isTrue(isIfGenericContainsMachine(derivedFrom,
configurationUnit));
this.derivedFrom = derivedFrom;
this.configurationUnit = configurationUnit;
this.dayAssignmentsState = new TransientState();
}
public MachineWorkersConfigurationUnit getConfigurationUnit() {
return configurationUnit;
}
public ResourceAllocation<?> getDerivedFrom() {
return derivedFrom;
}
public void resetAssignmentsTo(List<DerivedDayAssignment> dayAssignments) {
dayAssignmentsState.resetAssignmentsTo(dayAssignments);
}
public DerivedAllocation asDerivedFrom(ResourceAllocation<?> allocation)
throws IllegalStateException {
if (!isNewObject()) {
throw new IllegalStateException(
"a "
+ DerivedAllocation.class.getSimpleName()
+ " that already exists can't change its derivedFrom property");
}
this.derivedFrom = allocation;
return this;
}
private void checkAreValid(List<DerivedDayAssignment> dayAssignments) {
for (DerivedDayAssignment each : dayAssignments) {
checkIsValid(each);
}
}
private void checkIsValid(DerivedDayAssignment dayAssingment) {
if (!dayAssingment.getAllocation().equals(this)) {
throw new IllegalArgumentException(dayAssingment
+ " is related to " + dayAssingment.getAllocation()
+ " instead of this: " + this);
}
}
public void resetAssignmentsTo(LocalDate startInclusive,
LocalDate endExclusive, List<DerivedDayAssignment> newAssignments) {
dayAssignmentsState.resetAssignmentsTo(startInclusive, endExclusive,
newAssignments);
}
private void detachAssignments(Collection<DerivedDayAssignment> toBeRemoved) {
for (DerivedDayAssignment each : toBeRemoved) {
each.detach();
}
}
public List<DerivedDayAssignment> getAssignments() {
return dayAssignmentsState.getAssignments();
}
public void useScenario(Scenario scenario) {
this.dayAssignmentsState = dayAssignmentsState.useScenario(scenario);
}
public Set<DerivedDayAssignmentsContainer> getContainers() {
return new HashSet<DerivedDayAssignmentsContainer>(
derivedDayAssignmentsContainers);
}
public void copyAssignments(Scenario from, Scenario to) {
DerivedDayAssignmentsContainer fromContainer = retrieveOrCreate(from);
DerivedDayAssignmentsContainer toContainer = retrieveOrCreate(to);
toContainer.resetAssignmentsTo(fromContainer.getDayAssignments());
}
public void removePredecessorContainersFor(Scenario scenario) {
Map<Scenario, DerivedDayAssignmentsContainer> byScenario = byScenario();
for (Scenario each : scenario.getPredecessors()) {
DerivedDayAssignmentsContainer container = byScenario.get(each);
if (container != null) {
derivedDayAssignmentsContainers.remove(container);
}
}
}
public void removeContainersFor(Scenario scenario) {
DerivedDayAssignmentsContainer container = byScenario().get(scenario);
if (container != null) {
derivedDayAssignmentsContainers.remove(container);
}
}
}