/*
* 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.web.resources.machine;
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.libreplan.business.calendars.daos.IBaseCalendarDAO;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.calendars.entities.CalendarData;
import org.libreplan.business.calendars.entities.ResourceCalendar;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.common.entities.Configuration;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.planner.daos.IDayAssignmentDAO;
import org.libreplan.business.planner.daos.IResourceAllocationDAO;
import org.libreplan.business.resources.daos.ICriterionDAO;
import org.libreplan.business.resources.daos.IMachineDAO;
import org.libreplan.business.resources.daos.IResourceDAO;
import org.libreplan.business.resources.daos.IWorkerDAO;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.CriterionSatisfaction;
import org.libreplan.business.resources.entities.Machine;
import org.libreplan.business.resources.entities.MachineWorkerAssignment;
import org.libreplan.business.resources.entities.MachineWorkersConfigurationUnit;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.ResourceEnum;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.scenarios.IScenarioManager;
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.web.common.IntegrationEntityModel;
import org.libreplan.web.common.concurrentdetection.OnConcurrentModification;
import org.libreplan.web.resources.search.ResourcePredicate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Diego Pino Garcia <dpino@igalia.com>
* @author Javier Moran Rua <jmoran@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@OnConcurrentModification(goToPage = "/resources/machine/machines.zul")
public class MachineModel extends IntegrationEntityModel implements IMachineModel {
private static Log LOG = LogFactory.getLog(MachineModel.class);
/**
* State field.
* Machine is the root of the aggregate for holding the state.
*
* Includes:
* The Machine instance and associated entities.
* The MachineWorkersConfigurationUnit set of the machine.
* The MachineWorkerAssignments of each MachineWorkersConfigurationUnit instance.
* The Criterion set required by each MachineWorkersConfigurationUnit instance.
* The calendar associated with the Machine instance.
*
*/
private Machine machine;
private ResourceCalendar calendarToRemove = null;
private Map<Long, Criterion> criterions = new HashMap<>();
private Map<Long, Worker> workers = new HashMap<>();
private List<Machine> machineList = new ArrayList<>();
@Autowired
private IResourceDAO resourceDAO;
@Autowired
private IMachineDAO machineDAO;
@Autowired
private IBaseCalendarDAO baseCalendarDAO;
@Autowired
private ICriterionDAO criterionDAO;
@Autowired
private IWorkerDAO workerDAO;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private IDayAssignmentDAO dayAssignmentDAO;
@Autowired
private IWorkReportLineDAO workReportLineDAO;
@Autowired
private IResourceAllocationDAO resourceAllocationDAO;
@Autowired
private IScenarioManager scenarioManager;
private void reattachCriterionsCache() {
for (Criterion each: criterions.values()) {
criterionDAO.reattachUnmodifiedEntity(each);
}
}
private void reattachWorkersCache() {
for (Worker each: workers.values()) {
workerDAO.reattachUnmodifiedEntity(each);
}
}
private void insertInCriterionsCacheIfNotExist(Criterion criterion) {
if (!criterions.containsValue(criterion)) {
criterions.put(criterion.getId(), criterion);
}
}
private void insertInWorkersCacheIfNotExist(Worker worker) {
if (!workers.containsValue(worker)) {
workers.put(worker.getId(), worker);
}
}
@Override
@Transactional(readOnly = true)
public void initCreate() {
machine = Machine.create("");
machine.setCodeAutogenerated(configurationDAO.getConfiguration().getGenerateCodeForResources());
if (machine.isCodeAutogenerated()) {
setDefaultCode();
}
}
@Override
@Transactional(readOnly = true)
public void initEdit(Machine machine) {
Validate.notNull(machine);
try {
this.machine = (Machine) resourceDAO.find(machine.getId());
loadDependentEntities();
initOldCodes();
} catch (InstanceNotFoundException e) {
LOG.error("Machine with id " + machine.getId() + " not found", e);
throw new RuntimeException();
}
}
private void loadDependentEntities() {
reattachCriterionsCache();
reattachWorkersCache();
loadCriterionSatisfactions();
loadConfigurationUnits();
loadCalendar();
}
private void loadCalendar() {
if (machine.getCalendar() != null) {
forceLoadCalendar(machine.getCalendar());
}
}
private void loadCriterionSatisfactions() {
for (CriterionSatisfaction each: machine.getCriterionSatisfactions()) {
each.getStartDate();
each.getCriterion().getCompleteName();
insertInCriterionsCacheIfNotExist(each.getCriterion());
}
}
private void loadConfigurationUnits() {
for (MachineWorkersConfigurationUnit each : machine.getConfigurationUnits()) {
each.getName();
loadRequiredCriterionsOf(each);
loadMachineWorkersAssignmentsOf(each);
}
}
private void loadRequiredCriterionsOf(MachineWorkersConfigurationUnit configurationUnit) {
for (Criterion each: configurationUnit.getRequiredCriterions()) {
each.getCompleteName();
insertInCriterionsCacheIfNotExist(each);
}
}
private void loadMachineWorkersAssignmentsOf(MachineWorkersConfigurationUnit configurationUnit) {
for (MachineWorkerAssignment each: configurationUnit.getWorkerAssignments()) {
each.getStartDate();
each.getWorker().getName();
insertInWorkersCacheIfNotExist(each.getWorker());
}
}
@Override
public Machine getMachine() {
return machine;
}
@Override
@Transactional(readOnly = true)
public List<MachineWorkersConfigurationUnit> getConfigurationUnitsOfMachine() {
ArrayList<MachineWorkersConfigurationUnit> elements = new ArrayList<>();
if (machine != null) {
elements.addAll(machine.getConfigurationUnits());
}
Collections.sort(elements);
return elements;
}
@Override
public void setCalendarOfMachine(ResourceCalendar resourceCalendar) {
if (machine != null) {
machine.setCalendar(resourceCalendar);
}
}
@Override
public ResourceCalendar getCalendarOfMachine() {
return (machine != null) ? machine.getCalendar() : null;
}
@Transactional(readOnly=true)
@Override
public void addWorkerAssignmentToConfigurationUnit(MachineWorkersConfigurationUnit unit, Worker worker) {
for (MachineWorkersConfigurationUnit each: machine.getConfigurationUnits()) {
if (each == unit) {
each.addNewWorkerAssignment(worker);
}
}
}
@Transactional(readOnly=true)
@Override
public void addCriterionRequirementToConfigurationUnit(MachineWorkersConfigurationUnit unit, Criterion criterion) {
HashSet<ResourceEnum> appliableToMachine = new HashSet<>();
appliableToMachine.add(ResourceEnum.MACHINE);
unit.addRequiredCriterion(criterion);
}
@Override
@Transactional
public void confirmSave() throws ValidationException {
removeCalendarIfNeeded();
resourceDAO.save(machine);
}
private void removeCalendarIfNeeded() {
if (calendarToRemove != null) {
try {
resourceDAO.reattach(machine);
baseCalendarDAO.remove(calendarToRemove.getId());
calendarToRemove = null;
} catch (InstanceNotFoundException e) {
LOG.error("Couldn't remove calendar");
}
}
}
@Override
@Transactional(readOnly = true)
public List<BaseCalendar> getBaseCalendars() {
return baseCalendarDAO.getBaseCalendars();
}
@Override
@Transactional(readOnly = true)
public List<Machine> getMachines() {
machineList = machineDAO.getAll();
return machineList;
}
public MachineWorkersConfigurationUnit getConfigurationUnitById(Long id) throws InstanceNotFoundException {
MachineWorkersConfigurationUnit unit = null;
for (MachineWorkersConfigurationUnit each : getConfigurationUnitsOfMachine()) {
if (each.getId().equals(id)) {
unit = each;
}
}
if (unit == null) {
throw new InstanceNotFoundException(id, MachineModel.class.getName());
}
return unit;
}
@Override
public void removeConfigurationUnit(MachineWorkersConfigurationUnit unit) {
machine.removeMachineWorkersConfigurationUnit(unit);
}
@Override
public void setCalendar(ResourceCalendar resourceCalendar) {
if (machine != null) {
machine.setCalendar(resourceCalendar);
}
}
@Override
public ResourceCalendar getCalendar() {
if (machine != null) {
return machine.getCalendar();
}
return null;
}
@Override
@Transactional(readOnly = true)
public BaseCalendar getDefaultCalendar() {
Configuration configuration = configurationDAO.getConfiguration();
if (configuration == null) {
return null;
}
BaseCalendar defaultCalendar = configuration.getDefaultCalendar();
forceLoadCalendar(defaultCalendar);
return defaultCalendar;
}
private void forceLoadCalendar(BaseCalendar baseCalendar) {
for (CalendarData calendarData : baseCalendar.getCalendarDataVersions()) {
calendarData.getHoursPerDay().size();
if (calendarData.getParent() != null) {
forceLoadCalendar(calendarData.getParent());
}
}
baseCalendar.getExceptions().size();
}
@Override
@Transactional(readOnly = true)
public List<Machine> getFilteredMachines(ResourcePredicate predicate) {
List<Machine> filteredResourceList = new ArrayList<>();
for (Machine machine : machineList) {
machineDAO.reattach(machine);
if (predicate.accepts(machine)) {
filteredResourceList.add(machine);
}
}
return filteredResourceList;
}
public List<Machine> getAllMachines() {
return machineList;
}
@Override
@Transactional(readOnly=true)
public boolean canRemove(Machine machine) {
List<Resource> resourcesList = new ArrayList<>();
resourcesList.add(machine);
return dayAssignmentDAO.findByResources(resourcesList).isEmpty() &&
workReportLineDAO.findByResources(resourcesList).isEmpty() &&
resourceAllocationDAO.findAllocationsRelatedToAnyOf(scenarioManager.getCurrent(), resourcesList).isEmpty();
}
@Override
@Transactional
public void confirmRemove(Machine machine) throws InstanceNotFoundException {
resourceDAO.remove(machine.getId());
}
public EntityNameEnum getEntityName() {
return EntityNameEnum.MACHINE;
}
public Set<IntegrationEntity> getChildren() {
return new HashSet<>();
}
public IntegrationEntity getCurrentEntity() {
return this.machine;
}
@Override
public void removeCalendar() {
calendarToRemove = machine.getCalendar();
machine.setCalendar(null);
}
@Override
public List<Worker> getWorkers() {
return workerDAO.getWorkers();
}
}