/* * This file is part of LibrePlan * * Copyright (C) 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.planner.order; import static org.libreplan.business.planner.entities.TaskElement.justTasks; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.hibernate.Hibernate; import org.joda.time.LocalDate; import org.libreplan.business.advance.entities.DirectAdvanceAssignment; import org.libreplan.business.advance.entities.IndirectAdvanceAssignment; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.daos.IEntitySequenceDAO; import org.libreplan.business.common.entities.EntityNameEnum; import org.libreplan.business.labels.entities.Label; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.entities.HoursGroup; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderStatusEnum; import org.libreplan.business.orders.entities.TaskSource; import org.libreplan.business.orders.entities.TaskSource.IOptionalPersistence; import org.libreplan.business.orders.entities.TaskSource.TaskSourceSynchronization; import org.libreplan.business.planner.daos.ITaskElementDAO; import org.libreplan.business.planner.daos.ITaskSourceDAO; import org.libreplan.business.planner.entities.AssignmentFunction; import org.libreplan.business.planner.entities.DayAssignment; import org.libreplan.business.planner.entities.DayAssignment.FilterType; import org.libreplan.business.planner.entities.Dependency; import org.libreplan.business.planner.entities.DerivedAllocation; import org.libreplan.business.planner.entities.GenericResourceAllocation; import org.libreplan.business.planner.entities.IMoneyCostCalculator; import org.libreplan.business.planner.entities.ResourceAllocation; import org.libreplan.business.planner.entities.ResourceAllocation.IVisitor; import org.libreplan.business.planner.entities.SpecificResourceAllocation; import org.libreplan.business.planner.entities.StretchesFunction; import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; import org.libreplan.business.planner.entities.TaskMilestone; import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidation; import org.libreplan.business.requirements.entities.CriterionRequirement; import org.libreplan.business.resources.daos.ICriterionDAO; import org.libreplan.business.resources.daos.IResourceDAO; import org.libreplan.business.resources.entities.Criterion; import org.libreplan.business.resources.entities.CriterionSatisfaction; import org.libreplan.business.resources.entities.IAssignmentsOnResourceCalculator; import org.libreplan.business.resources.entities.Resource; import org.libreplan.business.scenarios.IScenarioManager; import org.libreplan.business.scenarios.daos.IOrderVersionDAO; import org.libreplan.business.scenarios.daos.IScenarioDAO; import org.libreplan.business.scenarios.entities.OrderVersion; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.users.daos.IOrderAuthorizationDAO; import org.libreplan.business.users.entities.OrderAuthorization; import org.libreplan.business.users.entities.ProfileOrderAuthorization; import org.libreplan.business.users.entities.User; import org.libreplan.business.users.entities.UserOrderAuthorization; import org.libreplan.web.UserUtil; import org.libreplan.web.calendars.BaseCalendarModel; import org.libreplan.web.planner.TaskElementAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties; import org.zkoss.ganttz.adapters.IStructureNavigator; import org.zkoss.ganttz.adapters.PlannerConfiguration; import org.zkoss.zk.ui.Desktop; /** * It retrieves the PlaningState from a ZK {@link Desktop}. * If it doesn't exist yet, it creates and initializes a new PlanningState. * * @author Óscar González Fernández <ogonzalez@igalia.com> * @author Lorenzo Tilve Álvaro <ltilve@igalia.com> * @author Vova Perebykivskyi <vova@libreplan-enterprise.com> */ @Component @Scope(BeanDefinition.SCOPE_SINGLETON) public class PlanningStateCreator { private static final String ATTRIBUTE_NAME = PlanningState.class.getName(); /** * When the scenario is not the owner, all the tasks are copied, creating new assignments. * But the previous assignments keep on being referenced by the resource and must be discarded. */ private static final class AvoidStaleAssignments implements IAssignmentsOnResourceCalculator { private Set<DayAssignment> previousAssignmentsSet; public AvoidStaleAssignments(List<DayAssignment> previousAssignments) { this.previousAssignmentsSet = new HashSet<>(previousAssignments); } @Override public List<DayAssignment> getAssignments(Resource resource) { List<DayAssignment> result = new ArrayList<>(); for (DayAssignment each : resource.getAssignments()) { if (!previousAssignmentsSet.contains(each)) { result.add(each); } } return result; } } @Autowired private IScenarioManager scenarioManager; @Autowired private IAdHocTransactionService transactionService; @Autowired private IOrderVersionDAO orderVersionDAO; @Autowired private IResourceDAO resourceDAO; @Autowired private ICriterionDAO criterionDAO; @Autowired private ITaskElementDAO taskDAO; @Autowired private IOrderDAO orderDAO; @Autowired private IScenarioDAO scenarioDAO; @Autowired private ITaskSourceDAO taskSourceDAO; @Autowired private IEntitySequenceDAO entitySequenceDAO; @Autowired private TaskElementAdapter taskElementAdapterCreator; @Autowired private SaveCommandBuilder saveCommandBuilder; @Autowired private IOrderAuthorizationDAO orderAuthorizationDAO; @Autowired private IMoneyCostCalculator moneyCostCalculator; private ISaveCommand cachedCommand; void synchronizeWithSchedule(Order order, IOptionalPersistence persistence) { List<TaskSourceSynchronization> synchronizationsNeeded = order.calculateSynchronizationsNeeded(); for (TaskSourceSynchronization each : synchronizationsNeeded) { each.apply(persistence); } } public interface IActionsOnRetrieval { void onRetrieval(PlanningState planningState); } public PlanningState createOn(Desktop desktop, Order order) { Validate.notNull(desktop); Validate.notNull(order); setupScenario(order); PlanningState result = createPlanning(order); desktop.setAttribute(ATTRIBUTE_NAME, result); return result; } void setupScenario(Order order) { if (!order.hasNoVersions()) { return; } Scenario currentScenario = scenarioManager.getCurrent(); OrderVersion orderVersion = currentScenario.addOrder(order); order.setVersionForScenario(currentScenario, orderVersion); order.useSchedulingDataFor(currentScenario); } public PlanningState retrieveOrCreate(Desktop desktop, Order order) { return retrieveOrCreate(desktop, order, null); } public PlanningState retrieveOrCreate(Desktop desktop, Order order, IActionsOnRetrieval onRetrieval) { Object existent = null; if (desktop != null) { existent = desktop.getAttribute(ATTRIBUTE_NAME); } if (existent instanceof PlanningState) { PlanningState result = (PlanningState) existent; // TODO resolve deprecated if (ObjectUtils.equals(order.getId(), result.getOrder().getId())) { result.onRetrieval(); if (onRetrieval != null) { onRetrieval.onRetrieval(result); } return result; } } PlanningState result = createPlanning(reload(order)); result.onRetrieval(); if (desktop != null) { desktop.setAttribute(ATTRIBUTE_NAME, result); } return result; } private Order reload(Order order) { ensureOrderVersionsAreNotProxies(); Order result = orderDAO.findExistingEntity(order.getId()); Scenario current = scenarioManager.getCurrent(); result.useSchedulingDataFor(current); return result; } private void ensureOrderVersionsAreNotProxies() { List<Long> orderVersionIds = transactionService.runOnAnotherReadOnlyTransaction(() -> { List<Long> result = new ArrayList<>(); Scenario scenario = scenarioManager.getCurrent(); for (OrderVersion each : scenario.getOrders().values()) { result.add(each.getId()); } return result; }); for (Long each : orderVersionIds) { orderVersionDAO.findExistingEntity(each); } } private PlanningState createPlanning(Order orderReloaded) { final List<Resource> allResources = resourceDAO.list(Resource.class); criterionDAO.list(Criterion.class); forceLoadOfOrderAssociatedData(orderReloaded); TaskGroup rootTask = orderReloaded.getAssociatedTaskElement(); if (rootTask != null) { forceLoadOf(rootTask); forceLoadDayAssignments(orderReloaded.getResources(FilterType.KEEP_ALL)); forceLoadOfDepedenciesCollections(rootTask); forceLoadOfLabels(Collections.singletonList(rootTask)); } if (orderReloaded.getCalendar() != null) { BaseCalendarModel.forceLoadBaseCalendar(orderReloaded.getCalendar()); } PlanningState result = new PlanningState(orderReloaded, allResources ); forceLoadOfWorkingHours(result.getInitial()); moneyCostCalculator.resetMoneyCostMap(); return result; } private void forceLoadOfOrderAssociatedData(Order order) { List<OrderElement> all = new ArrayList<>(); all.add(order); all.addAll(order.getAllChildren()); for (OrderElement each : all) { for (DirectAdvanceAssignment direct : each.getDirectAdvanceAssignments()) { direct.getAdvanceMeasurements().size(); direct.getAdvanceType().getHumanId(); direct.getAdvanceType().getUnitName(); } for (IndirectAdvanceAssignment indirect : each.getIndirectAdvanceAssignments()) { indirect.getAdvanceType().getUnitName(); Set<CalculatedConsolidation> consolidation = indirect.getCalculatedConsolidation(); for (CalculatedConsolidation c : consolidation) { c.getCalculatedConsolidatedValues().size(); } } for (HoursGroup hours : each.getHoursGroups()) { for (CriterionRequirement requirement : hours.getCriterionRequirements()) { requirement.ensureDataLoaded(); } hours.getValidCriterions().size(); } } } private void forceLoadDayAssignments(Set<Resource> resources) { for (Resource resource : resources) { resource.getAssignments().size(); } } private void forceLoadOf(TaskElement taskElement) { forceLoadOfDataAssociatedTo(taskElement); if (taskElement instanceof TaskGroup) { findChildrenWithQueryToAvoidProxies((TaskGroup) taskElement); for (TaskElement each : taskElement.getChildren()) { forceLoadOf(each); } } } private void forceLoadOfDataAssociatedTo(TaskElement each) { forceLoadOfResourceAllocationsResourcesAndAssignmentFunction(each); forceLoadOfCriterions(each); forceLoadOfSubcontractedTaskData(each); BaseCalendar calendar = each.getOwnCalendar(); if (calendar == null && each.getOrderElement() != null) { calendar = orderDAO.loadOrderAvoidingProxyFor(each.getOrderElement()).getCalendar(); } if (calendar != null) { BaseCalendarModel.forceLoadBaseCalendar(calendar); } each.hasConsolidations(); } /** * Forcing the load of all resources so the resources at planning state and at allocations are the same. * It loads the assignment function too. */ private static void forceLoadOfResourceAllocationsResourcesAndAssignmentFunction(TaskElement taskElement) { Set<ResourceAllocation<?>> resourceAllocations = taskElement.getAllResourceAllocations(); for (ResourceAllocation<?> each : resourceAllocations) { each.getAssociatedResources(); for (DerivedAllocation eachDerived : each.getDerivedAllocations()) { forceLoadOfDerivedAllocation(eachDerived); } forceLoadOfAssignmentFunction(each); } } private static void forceLoadOfDerivedAllocation(DerivedAllocation derivedAllocation) { derivedAllocation.getResources(); derivedAllocation.getConfigurationUnit().getWorkerAssignments().size(); } private static void forceLoadOfAssignmentFunction(ResourceAllocation<?> each) { AssignmentFunction function = each.getAssignmentFunction(); if ((function != null) && (function instanceof StretchesFunction)) { ((StretchesFunction) function).getStretches().size(); } } /** * Forcing the load of all criterions so there are no different criterion * instances for the same criteiron at database */ private static void forceLoadOfCriterions(TaskElement taskElement) { List<GenericResourceAllocation> generic = ResourceAllocation.getOfType( GenericResourceAllocation.class, taskElement.getSatisfiedResourceAllocations()); for (GenericResourceAllocation each : generic) { for (Criterion eachCriterion : each.getCriterions()) { eachCriterion.getName(); } } } private static void forceLoadOfSubcontractedTaskData(TaskElement taskElement){ if ( taskElement.isTask() && ((Task)taskElement).getSubcontractedTaskData() != null ) { ((Task) taskElement) .getSubcontractedTaskData() .getRequiredDeliveringDates() .forEach(SubcontractorDeliverDate::getSaveDate); } } private void findChildrenWithQueryToAvoidProxies(TaskGroup group) { for (TaskElement eachTask : taskDAO.findChildrenOf(group)) { Hibernate.initialize(eachTask); eachTask.getParent().getName(); } } private IScenarioInfo buildScenarioInfo(Order orderReloaded) { Scenario currentScenario = scenarioManager.getCurrent(); if (orderReloaded.isUsingTheOwnerScenario()) { switchAllocationsToScenario(currentScenario, orderReloaded.getAssociatedTaskElement()); return new UsingOwnerScenario(currentScenario, orderReloaded); } final List<DayAssignment> previousAssignments = orderReloaded .getDayAssignments(DayAssignment.FilterType.KEEP_ALL); OrderVersion newVersion = OrderVersion.createInitialVersion(currentScenario); orderReloaded.writeSchedulingDataChangesTo(currentScenario, newVersion); switchAllocationsToScenario(currentScenario, orderReloaded.getAssociatedTaskElement()); return new UsingNotOwnerScenario( new AvoidStaleAssignments(previousAssignments), orderReloaded, currentScenario, newVersion); } private static void switchAllocationsToScenario(Scenario scenario, TaskElement task) { if (task == null) { return; } for (ResourceAllocation<?> each : task.getAllResourceAllocations()) { each.switchToScenario(scenario); } } private void forceLoadOfDepedenciesCollections(TaskElement task) { loadDependencies(task.getDependenciesWithThisOrigin()); loadDependencies(task.getDependenciesWithThisDestination()); for (TaskElement each : task.getChildren()) { forceLoadOfDepedenciesCollections(each); } } private void loadDependencies(Set<Dependency> dependenciesWithThisOrigin) { for (Dependency each : dependenciesWithThisOrigin) { each.getOrigin().getName(); each.getDestination().getName(); } } private void forceLoadOfWorkingHours(List<TaskElement> initial) { for (TaskElement taskElement : initial) { if (taskElement.getTaskSource() != null) { taskElement.getTaskSource().getTotalHours(); OrderElement orderElement = taskElement.getOrderElement(); if (orderElement != null) { orderElement.getWorkHours(); } if (!taskElement.isLeaf()) { forceLoadOfWorkingHours(taskElement.getChildren()); } } } } private void forceLoadOfLabels(List<TaskElement> initial) { for (TaskElement taskElement : initial) { OrderElement orderElement = taskElement.getOrderElement(); if (orderElement != null) { Set<Label> labels = orderElement.getLabels(); for (Label each : labels) { each.getType().getName(); } } forceLoadOfLabels(taskElement.getChildren()); } } private static final class TaskElementNavigator implements IStructureNavigator<TaskElement> { @Override public List<TaskElement> getChildren(TaskElement object) { return object.getChildren(); } @Override public boolean isLeaf(TaskElement object) { return object.isLeaf(); } @Override public boolean isMilestone(TaskElement object) { return object != null && object instanceof TaskMilestone; } } public interface IScenarioInfo { IAssignmentsOnResourceCalculator getAssignmentsCalculator(); Scenario getCurrentScenario(); boolean isUsingTheOwnerScenario(); /** * @throws IllegalStateException * if it's using the owner scenario */ void saveVersioningInfo() throws IllegalStateException; void afterCommit(); } private class ChangeScenarioInfoOnSave implements IScenarioInfo { private IScenarioInfo current; private final Order order; public ChangeScenarioInfoOnSave(IScenarioInfo initial, Order order) { Validate.notNull(initial); Validate.notNull(order); this.current = initial; this.order = order; } public IAssignmentsOnResourceCalculator getAssignmentsCalculator() { return current.getAssignmentsCalculator(); } public Scenario getCurrentScenario() { return current.getCurrentScenario(); } public boolean isUsingTheOwnerScenario() { return current.isUsingTheOwnerScenario(); } public void saveVersioningInfo() throws IllegalStateException { current.saveVersioningInfo(); } public void afterCommit() { if (current instanceof ChangeScenarioInfoOnSave) { current = new UsingOwnerScenario(current.getCurrentScenario(), order, current.getAssignmentsCalculator()); } } } private class UsingOwnerScenario implements IScenarioInfo { private final Scenario currentScenario; private final Order order; private final IAssignmentsOnResourceCalculator calculator; public UsingOwnerScenario(Scenario currentScenario, Order order) { this(currentScenario, order, new Resource.AllResourceAssignments()); } public UsingOwnerScenario(Scenario currentScenario, Order order, IAssignmentsOnResourceCalculator calculator) { Validate.notNull(currentScenario); Validate.notNull(order); this.currentScenario = currentScenario; this.order = order; this.calculator = calculator; } @Override public boolean isUsingTheOwnerScenario() { return true; } @Override public void saveVersioningInfo() { OrderVersion orderVersion = order.getCurrentVersionInfo().getOrderVersion(); if (order.isNewObject()) { scenarioDAO.updateDerivedScenariosWithNewVersion(null, order, currentScenario, orderVersion); } orderVersion.savingThroughOwner(); synchronizeWithSchedule(order, getPersistence()); order.writeSchedulingDataChanges(); } IOptionalPersistence getPersistence() { return order.isNewObject() ? TaskSource.persistButDontRemoveTaskSources(taskSourceDAO) : TaskSource.persistTaskSources(taskSourceDAO); } @Override public void afterCommit() { // Do nothing } @Override public Scenario getCurrentScenario() { return currentScenario; } @Override public IAssignmentsOnResourceCalculator getAssignmentsCalculator() { return calculator; } } private class UsingNotOwnerScenario implements IScenarioInfo { private final Scenario currentScenario; private final OrderVersion newVersion; private final Order order; private final IAssignmentsOnResourceCalculator assignmentsOnResourceCalculator; public UsingNotOwnerScenario( IAssignmentsOnResourceCalculator assignmentsOnResourceCalculator, Order order, Scenario currentScenario, OrderVersion newVersion) { Validate.notNull(assignmentsOnResourceCalculator); Validate.notNull(order); Validate.notNull(currentScenario); Validate.notNull(newVersion); this.assignmentsOnResourceCalculator = assignmentsOnResourceCalculator; this.currentScenario = currentScenario; this.newVersion = newVersion; this.order = order; } @Override public boolean isUsingTheOwnerScenario() { return false; } @Override public void saveVersioningInfo() throws IllegalStateException { reattachAllTaskSources(); createAndSaveNewOrderVersion(scenarioManager.getCurrent(), newVersion); synchronizeWithSchedule(order, TaskSource.persistButDontRemoveTaskSources(taskSourceDAO)); order.writeSchedulingDataChanges(); } private void createAndSaveNewOrderVersion(Scenario currentScenario, OrderVersion newOrderVersion) { OrderVersion previousOrderVersion = currentScenario.getOrderVersion(order); currentScenario.setOrderVersion(order, newOrderVersion); scenarioDAO.updateDerivedScenariosWithNewVersion( previousOrderVersion, order, currentScenario, newOrderVersion); } private void reattachAllTaskSources() { // Avoid LazyInitializationException for when doing removePredecessorsDayAssignmentsFor for (TaskSource each : order.getAllScenariosTaskSourcesFromBottomToTop()) { taskSourceDAO.reattach(each); } } @Override public void afterCommit() {} @Override public Scenario getCurrentScenario() { return currentScenario; } @Override public IAssignmentsOnResourceCalculator getAssignmentsCalculator() { return assignmentsOnResourceCalculator; } } public class PlanningState { private final Order order; private ArrayList<TaskElement> initial; private Set<TaskElement> toRemove = new HashSet<>(); private Set<Resource> resources = new HashSet<>(); private final IScenarioInfo scenarioInfo; private List<OrderAuthorization> orderAuthorizations; private List<OrderAuthorization> orderAuthorizationsAddition = new ArrayList<>(); private List<OrderAuthorization> orderAuthorizationsRemoval = new ArrayList<>(); private OrderStatusEnum savedOrderState; private PlannerConfiguration<TaskElement> cachedConfiguration; public PlanningState(Order order, Collection<? extends Resource> initialResources) { Validate.notNull(order); this.order = order; rebuildTasksState(); this.scenarioInfo = new ChangeScenarioInfoOnSave(buildScenarioInfo(order), order); this.resources = OrderPlanningModel.loadRequiredDataFor(new HashSet<>(initialResources)); associateWithScenario(this.resources); this.orderAuthorizations = loadOrderAuthorizations(); this.savedOrderState = order.getState(); } private List<OrderAuthorization> loadOrderAuthorizations() { if (order.isNewObject()) { return new ArrayList<>(); } List<OrderAuthorization> orderAuthorizations = orderAuthorizationDAO.listByOrder(order); for (OrderAuthorization each : orderAuthorizations) { if (each instanceof UserOrderAuthorization) { ((UserOrderAuthorization) each).getUser().getLoginName(); } if (each instanceof ProfileOrderAuthorization) { ((ProfileOrderAuthorization) each).getProfile().getProfileName(); } } return orderAuthorizations; } void onRetrieval() { cachedConfiguration = null; cachedCommand = null; synchronizeScheduling(); generateOrderElementCodes(); rebuildTasksState(); } void synchronizeScheduling() { synchronizeWithSchedule(order, TaskSource.dontPersist()); } private void generateOrderElementCodes() { order.generateOrderElementCodes(entitySequenceDAO.getNumberOfDigitsCode(EntityNameEnum.ORDER)); } private void rebuildTasksState() { TaskGroup rootTask = getRootTask(); if (rootTask == null) { this.initial = new ArrayList<>(); } else { this.initial = new ArrayList<>(rootTask.getChildren()); } } private void associateWithScenario(Collection<? extends Resource> resources) { Scenario currentScenario = getCurrentScenario(); for (Resource each : resources) { each.useScenario(currentScenario); } } public Order getOrder() { return order; } public boolean isEmpty() { return getRootTask() == null; } /** * <p> * When the scenario was not owner, the previous {@link DayAssignment} day assignments * for the scenario must be avoided. * Since the previous scenario was not an owner, all tasks and related information are * copied, but the resource keeps pointing to the scenario's previous assignments. * </p> * * <p> * If the scenario is the owner, the assignments are returned directly. * </p> * * @return the {@link IAssignmentsOnResourceCalculator} to use. * @see IAssignmentsOnResourceCalculator * @see AvoidStaleAssignments */ public IAssignmentsOnResourceCalculator getAssignmentsCalculator() { return getScenarioInfo().getAssignmentsCalculator(); } public PlannerConfiguration<TaskElement> getConfiguration() { if (cachedConfiguration != null) { return cachedConfiguration; } IAdapterToTaskFundamentalProperties<TaskElement> adapter; adapter = taskElementAdapterCreator.createForOrder(getScenarioInfo().getCurrentScenario(), order, this); PlannerConfiguration<TaskElement> result = new PlannerConfiguration<>(adapter, new TaskElementNavigator(), getInitial()); result.setNotBeforeThan(order.getInitDate()); result.setNotAfterThan(order.getDeadline()); result.setDependenciesConstraintsHavePriority(order.getDependenciesConstraintsHavePriority()); result.setScheduleBackwards(order.isScheduleBackwards()); cachedConfiguration = result; return cachedConfiguration; } public ISaveCommand getSaveCommand() { if (cachedCommand != null) { return cachedCommand; } cachedCommand = saveCommandBuilder.build(this, getConfiguration()); return cachedCommand; } public List<TaskElement> getInitial() { return new ArrayList<>(initial); } public List<Task> getAllTasks() { List<Task> result = new ArrayList<>(); if (getRootTask() != null) { findTasks(getRootTask(), result); } return result; } private void findTasks(TaskElement taskElement, List<Task> result) { if (taskElement instanceof Task) { Task t = (Task) taskElement; result.add(t); } for (TaskElement each : taskElement.getChildren()) { findTasks(each, result); } } public void reassociateResourcesWithSession() { User user = UserUtil.getUserFromSession(); boolean isBoundUser = (user != null) && user.isBound(); for (Resource resource : resources) { if (isBoundUser && user.getWorker().getId().equals(resource.getId())) { // Resource bound to current user is already associated with session continue; } resourceDAO.reattach(resource); } // Ensuring no repeated instances of criterions reattachCriterions(getExistentCriterions(resources)); addingNewlyCreated(resourceDAO); } private Set<Criterion> getExistentCriterions(Set<Resource> resources) { Set<Criterion> result = new HashSet<>(); for (Resource resource : resources) { for (CriterionSatisfaction each : resource.getCriterionSatisfactions()) { result.add(each.getCriterion()); } } return result; } private void reattachCriterions(Set<Criterion> criterions) { for (Criterion each : criterions) { if (!Hibernate.isInitialized(each)) { criterionDAO.reattachUnmodifiedEntity(each); } } } private void addingNewlyCreated(IResourceDAO resourceDAO) { Set<Resource> newResources = getNewResources(resourceDAO); OrderPlanningModel.loadRequiredDataFor(newResources); associateWithScenario(newResources); resources.addAll(newResources); } private Set<Resource> getNewResources(IResourceDAO resourceDAO) { Set<Resource> result = new HashSet<>(resourceDAO.list(Resource.class)); result.removeAll(resources); return result; } public Collection<? extends TaskElement> getToRemove() { return Collections.unmodifiableCollection(onlyNotTransient(toRemove)); } private List<TaskElement> onlyNotTransient(Collection<? extends TaskElement> toRemove) { ArrayList<TaskElement> result = new ArrayList<>(); for (TaskElement taskElement : toRemove) { if (taskElement.getId() != null) { result.add(taskElement); } } return result; } public void removed(TaskElement taskElement) { taskElement.detach(); if (!isTopLevel(taskElement)) { return; } toRemove.add(taskElement); } private boolean isTopLevel(TaskElement taskElement) { return taskElement instanceof TaskMilestone || taskElement.getParent() == getRootTask(); } public TaskGroup getRootTask() { return order.getAssociatedTaskElement(); } public IScenarioInfo getScenarioInfo() { return scenarioInfo; } public Scenario getCurrentScenario() { return getScenarioInfo().getCurrentScenario(); } public void reattach() { orderDAO.reattach(order); if (getRootTask() != null) { taskDAO.reattach(getRootTask()); } } public void synchronizeTrees() { scenarioInfo.saveVersioningInfo(); } public List<Resource> getResourcesRelatedWithAllocations() { Set<Resource> result = new HashSet<>(); for (Task each : justTasks(order.getAllChildrenAssociatedTaskElements())) { result.addAll(resourcesRelatedWith(each)); } return new ArrayList<>(result); } private Set<Resource> resourcesRelatedWith(Task task) { Set<Resource> result = new HashSet<>(); for (ResourceAllocation<?> each : task.getSatisfiedResourceAllocations()) { result.addAll(resourcesRelatedWith(each)); } return result; } private Collection<Resource> resourcesRelatedWith(ResourceAllocation<?> allocation) { return ResourceAllocation.visit(allocation, new IVisitor<Collection<Resource>>() { @Override public Collection<Resource> on(SpecificResourceAllocation specificAllocation) { return Collections.singletonList(specificAllocation.getResource()); } @Override public Collection<Resource> on(GenericResourceAllocation genericAllocation) { return DayAssignment.byResource(genericAllocation.getAssignments()).keySet(); } }); } public List<ResourceAllocation<?>> replaceByCurrentOnes( Collection<? extends ResourceAllocation<?>> allocationsReturnedByQuery, IAllocationCriteria allocationCriteria) { Set<Long> orderElements = getIds(order.getAllChildren()); List<ResourceAllocation<?>> result = allocationsNotInOrder(allocationsReturnedByQuery, orderElements); result.addAll(allocationsInOrderSatisfyingCriteria( order.getAllChildrenAssociatedTaskElements(), allocationCriteria)); return result; } private Set<Long> getIds(List<OrderElement> allChildren) { Set<Long> result = new HashSet<>(); for (OrderElement each : allChildren) { result.add(each.getId()); } return result; } private List<ResourceAllocation<?>> allocationsNotInOrder( Collection<? extends ResourceAllocation<?>> allocationsReturnedByQuery, Set<Long> orderElementsIds) { List<ResourceAllocation<?>> result = new ArrayList<>(); for (ResourceAllocation<?> each : allocationsReturnedByQuery) { Task task = each.getTask(); if (!isIncluded(orderElementsIds, task)) { result.add(each); } } return result; } private boolean isIncluded(Set<Long> orderElementsIds, Task task) { return orderElementsIds.contains(task.getOrderElement().getId()); } private List<ResourceAllocation<?>> allocationsInOrderSatisfyingCriteria( Collection<? extends TaskElement> tasks, IAllocationCriteria allocationCriteria) { List<ResourceAllocation<?>> result = new ArrayList<>(); for (Task each : justTasks(tasks)) { result.addAll(satisfying(allocationCriteria, each.getSatisfiedResourceAllocations())); } return result; } private List<ResourceAllocation<?>> satisfying( IAllocationCriteria criteria, Collection<ResourceAllocation<?>> allocations) { List<ResourceAllocation<?>> result = new ArrayList<>(); for (ResourceAllocation<?> each : allocations) { if (criteria.isSatisfiedBy(each)) { result.add(each); } } return result; } public List<OrderAuthorization> getOrderAuthorizations() { return Collections.unmodifiableList(orderAuthorizations); } public List<OrderAuthorization> getOrderAuthorizationsAddition() { return Collections.unmodifiableList(orderAuthorizationsAddition); } public List<OrderAuthorization> getOrderAuthorizationsRemoval() { return Collections.unmodifiableList(orderAuthorizationsRemoval); } public void addOrderAuthorization(OrderAuthorization orderAuthorization) { orderAuthorizations.add(orderAuthorization); orderAuthorizationsAddition.add(orderAuthorization); } public void removeOrderAuthorization(OrderAuthorization orderAuthorization) { orderAuthorizations.remove(orderAuthorization); orderAuthorizationsAddition.remove(orderAuthorization); if (!orderAuthorization.isNewObject()) { orderAuthorizationsRemoval.add(orderAuthorization); } } public void cleanOrderAuthorizationsAdditionAndRemoval() { orderAuthorizationsAddition.clear(); orderAuthorizationsRemoval.clear(); } public OrderStatusEnum getSavedOrderState() { return savedOrderState; } public void updateSavedOrderState() { savedOrderState = order.getState(); } } public interface IAllocationCriteria { boolean isSatisfiedBy(ResourceAllocation<?> resourceAllocation); } public static IAllocationCriteria and(final IAllocationCriteria... criteria) { return resourceAllocation -> { for (IAllocationCriteria each : criteria) { if (!each.isSatisfiedBy(resourceAllocation)) { return false; } } return true; }; } public static class TaskOnInterval implements IAllocationCriteria { private final LocalDate startInclusive; private final LocalDate endInclusive; public TaskOnInterval(LocalDate startInclusive, LocalDate endInclusive) { this.startInclusive = startInclusive; this.endInclusive = endInclusive; } @Override public boolean isSatisfiedBy(ResourceAllocation<?> resourceAllocation) { return !(startInclusive != null && resourceAllocation.getEndDate().compareTo(startInclusive) < 0) && !(endInclusive != null && resourceAllocation.getStartDate().compareTo(endInclusive) > 0); } } public static class RelatedWith implements IAllocationCriteria { private final Criterion criterion; public RelatedWith(Criterion criterion) { this.criterion = criterion; } @Override public boolean isSatisfiedBy(ResourceAllocation<?> resourceAllocation) { if (resourceAllocation instanceof GenericResourceAllocation) { GenericResourceAllocation g = (GenericResourceAllocation) resourceAllocation; Set<Criterion> allocationCriterions = g.getCriterions(); return someCriterionIn(allocationCriterions); } return false; } private boolean someCriterionIn(Collection<? extends Criterion> allocationCriterions) { for (Criterion each : allocationCriterions) { if (criterion.equals(each)) { return true; } } return false; } } public static class SpecificRelatedWithCriterionOnInterval implements IAllocationCriteria { private final LocalDate startInclusive; private final LocalDate endExclusive; private final Criterion criterion; public SpecificRelatedWithCriterionOnInterval(Criterion criterion, LocalDate startInclusive, LocalDate endInclusive) { Validate.notNull(criterion); this.startInclusive = startInclusive; this.endExclusive = endInclusive != null ? endInclusive.plusDays(1) : null; this.criterion = criterion; } @Override public boolean isSatisfiedBy(ResourceAllocation<?> resourceAllocation) { if (resourceAllocation instanceof SpecificResourceAllocation) { SpecificResourceAllocation s = (SpecificResourceAllocation) resourceAllocation; return s.interferesWith(criterion, startInclusive, endExclusive); } return false; } } public static class RelatedWithResource implements IAllocationCriteria { private final Resource resource; public RelatedWithResource(Resource resource) { Validate.notNull(resource); this.resource = resource; } @Override public boolean isSatisfiedBy(ResourceAllocation<?> resourceAllocation) { return ResourceAllocation.visit(resourceAllocation, new IVisitor<Boolean>() { @Override public Boolean on(SpecificResourceAllocation specificAllocation) { return specificAllocation.getResource().equals(resource); } @Override public Boolean on(GenericResourceAllocation genericAllocation) { return DayAssignment.byResource(genericAllocation.getAssignments()).containsKey(resource); } }); } } }