Package org.libreplan.web.resourceload

Source Code of org.libreplan.web.resourceload.ResourceLoadModel$ByCriterionFinder

/*
* 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.resourceload;

import static org.libreplan.business.planner.entities.TaskElement.justTasks;
import static org.libreplan.web.I18nHelper._;
import static org.libreplan.web.planner.order.PlanningStateCreator.and;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;

import org.joda.time.LocalDate;
import org.libreplan.business.calendars.daos.IBaseCalendarDAO;
import org.libreplan.business.calendars.entities.ResourceCalendar;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.planner.daos.IResourceAllocationDAO;
import org.libreplan.business.planner.entities.DayAssignment;
import org.libreplan.business.planner.entities.GenericResourceAllocation;
import org.libreplan.business.planner.entities.ResourceAllocation;
import org.libreplan.business.planner.entities.SpecificResourceAllocation;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.resources.daos.ICriterionDAO;
import org.libreplan.business.resources.daos.IResourceDAO;
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.scenarios.IScenarioManager;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.users.daos.IOrderAuthorizationDAO;
import org.libreplan.business.users.daos.IUserDAO;
import org.libreplan.business.users.entities.OrderAuthorization;
import org.libreplan.business.users.entities.OrderAuthorizationType;
import org.libreplan.business.users.entities.User;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.web.calendars.BaseCalendarModel;
import org.libreplan.web.planner.order.PlanningStateCreator.IAllocationCriteria;
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
import org.libreplan.web.planner.order.PlanningStateCreator.RelatedWith;
import org.libreplan.web.planner.order.PlanningStateCreator.RelatedWithResource;
import org.libreplan.web.planner.order.PlanningStateCreator.SpecificRelatedWithCriterionOnInterval;
import org.libreplan.web.planner.order.PlanningStateCreator.TaskOnInterval;
import org.libreplan.web.resourceload.ResourceLoadParameters.IReattacher;
import org.libreplan.web.resourceload.ResourceLoadParameters.Paginator;
import org.libreplan.web.security.SecurityUtils;
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.springframework.transaction.annotation.Transactional;
import org.zkoss.ganttz.data.GanttDate;
import org.zkoss.ganttz.data.resourceload.LoadPeriod;
import org.zkoss.ganttz.data.resourceload.LoadTimeLine;
import org.zkoss.ganttz.data.resourceload.TimeLineRole;

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ResourceLoadModel implements IResourceLoadModel {

    @Autowired
    private IResourceDAO resourcesDAO;

    @Autowired
    private IResourcesSearcher resourcesSearchModel;

    @Autowired
    private ICriterionDAO criterionDAO;

    @Autowired
    private IOrderDAO orderDAO;

    @Autowired
    private IResourceAllocationDAO resourceAllocationDAO;

    @Autowired
    private IUserDAO userDAO;

    @Autowired
    private IOrderAuthorizationDAO orderAuthorizationDAO;

    @Autowired
    private IScenarioManager scenarioManager;

    @Autowired
    private IBaseCalendarDAO baseCalendarDAO;

    @Autowired
    private IAdHocTransactionService transactionService;

    @Override
    @Transactional(readOnly = true)
    public ResourceLoadDisplayData calculateDataToDisplay(
            ResourceLoadParameters parameters) {

        PlanningState planningState = parameters.getPlanningState();
        if (planningState != null) {
            planningState.reattach();
            planningState.reassociateResourcesWithSession();
        }
        ResourceAllocationsFinder<?> allocationsFinder = create(parameters);
        List<LoadTimeLine> loadTimeLines = allocationsFinder.buildTimeLines();
        return new ResourceLoadDisplayData(loadTimeLines,
                parameters.getInitDateFilter(),
                parameters.getEndDateFilter(), allocationsFinder.getPaginator(),
                allocationsFinder.lazilyGetResourcesIncluded(),
                allocationsFinder.lazilyGetAssignmentsShown());
    }


    @Override
    @Transactional(readOnly = true)
    public Order getOrderByTask(TaskElement task) {
        Order result = orderDAO.loadOrderAvoidingProxyFor(task
                .getOrderElement());
        result.useSchedulingDataFor(scenarioManager.getCurrent());
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public boolean userCanRead(Order order, String loginName) {
        if (SecurityUtils.isSuperuserOrUserInRoles(
                UserRole.ROLE_READ_ALL_PROJECTS,
                UserRole.ROLE_EDIT_ALL_PROJECTS)) {
            return true;
        }
        try {
            User user = userDAO.findByLoginName(loginName);
            for (OrderAuthorization authorization : orderAuthorizationDAO
                    .listByOrderUserAndItsProfiles(order, user)) {
                if (authorization.getAuthorizationType() == OrderAuthorizationType.READ_AUTHORIZATION
                        || authorization.getAuthorizationType() == OrderAuthorizationType.WRITE_AUTHORIZATION) {
                    return true;
                }
            }
        } catch (InstanceNotFoundException e) {
            // this case shouldn't happen, because it would mean that there
            // isn't a logged user
            // anyway, if it happenned we don't allow the user to pass
        }
        return false;
    }

    public ResourceAllocationsFinder<?> create(ResourceLoadParameters parameters) {
        return parameters.isFilterByResources() ? new ByResourceFinder(
                parameters) : new ByCriterionFinder(parameters);
    }

    private abstract class ResourceAllocationsFinder<T extends BaseEntity> {

        protected final ResourceLoadParameters parameters;

        private ResourceAllocationsFinder(ResourceLoadParameters parameters) {
            this.parameters = parameters;
        }

        public Callable<List<Resource>> lazilyGetResourcesIncluded() {
            return new Callable<List<Resource>>() {

                @Override
                public List<Resource> call() throws Exception {
                    return reattach(getResourcesIncluded());
                }

                private List<Resource> reattach(List<Resource> resources) {
                    for (Resource resource : resources) {
                        ResourceCalendar calendar = resource.getCalendar();
                        BaseCalendarModel.forceLoadBaseCalendar(calendar);
                        resource.getAssignments().size();
                    }
                    return resources;
                }

            };
        }

        abstract List<Resource> getResourcesIncluded();

        public Callable<List<DayAssignment>> lazilyGetAssignmentsShown() {
            return new Callable<List<DayAssignment>>() {

                @Override
                public List<DayAssignment> call() throws Exception {
                    return getAssignmentsShown();
                }

            };
        }

        private List<DayAssignment> getAssignmentsShown() {
            Set<DayAssignment> result = new HashSet<DayAssignment>();
            Map<T, List<ResourceAllocation<?>>> foundAllocations = getFoundAllocations();
            for (Entry<T, List<ResourceAllocation<?>>> each : foundAllocations
                    .entrySet()) {
                for (ResourceAllocation<?> eachAllocation : each.getValue()) {
                    result.addAll(eachAllocation.getAssignments());
                }
            }
            return new ArrayList<DayAssignment>(result);
        }

        abstract List<LoadTimeLine> buildTimeLines();

        abstract Map<T, List<ResourceAllocation<?>>> getFoundAllocations();

        abstract Paginator<T> getPaginator();

        Scenario getCurrentScenario() {
            PlanningState state = parameters.getPlanningState();
            if (state != null) {
                return state.getCurrentScenario();
            }
            return scenarioManager.getCurrent();
        }

        Collection<? extends ResourceAllocation<?>> doReplacementsIfNeeded(
                Collection<? extends ResourceAllocation<?>> allocations,
                IAllocationCriteria criteria) {
            if (parameters.getPlanningState() == null) {
                return allocations;
            }
            return parameters.getPlanningState().replaceByCurrentOnes(
                    allocations, criteria);
        }

        protected IAllocationCriteria onInterval() {
            return new TaskOnInterval(parameters.getInitDateFilter(),
                    parameters.getEndDateFilter());
        }

    }

    private class ByResourceFinder extends ResourceAllocationsFinder<Resource> {

        private final Map<Resource, List<ResourceAllocation<?>>> allocationsByResource;
        private Paginator<Resource> resources;

        public ByResourceFinder(ResourceLoadParameters parameters) {
            super(parameters);
            this.resources = resourcesToShow();
            this.allocationsByResource = eachWithAllocations(this.resources
                    .getForCurrentPage());
        }

        @Override
        Paginator<Resource> getPaginator() {
            return resources;
        }

        @Override
        List<Resource> getResourcesIncluded() {
            return resources.getForCurrentPage();
        }

        @Override
        Map<Resource, List<ResourceAllocation<?>>> getFoundAllocations() {
            return allocationsByResource;
        }

        @Override
        List<LoadTimeLine> buildTimeLines() {
            return new ByResourceLoadTimesLinesBuilder(parameters)
                    .buildGroupsByResource(getFoundAllocations());
        }

        private Paginator<Resource> resourcesToShow() {
            return parameters.getEntities(Resource.class,
                    new Callable<List<Resource>>() {

                        @Override
                        public List<Resource> call() throws Exception {
                            if (parameters.thereIsCurrentOrder()) {
                                return resourcesForActiveTasks();
                            } else {
                                return allResourcesActiveBetween(
                                        parameters.getInitDateFilter(),
                                        parameters.getEndDateFilter());
                            }
                        }

                        private List<Resource> resourcesForActiveTasks() {
                            return Resource.sortByName(parameters
                                    .getPlanningState()
                                    .getResourcesRelatedWithAllocations());
                        }

                        private List<Resource> allResourcesActiveBetween(
                                LocalDate startDate, LocalDate endDate) {
                            List<Resource> allResources = allResources();
                            if (startDate == null && endDate == null) {
                                return allResources;
                            }

                            List<Resource> resources = new ArrayList<Resource>();
                            for (Resource resource : allResources) {
                                if (resource
                                        .isActiveBetween(startDate, endDate)) {
                                    resources.add(resource);
                                }
                            }
                            return resources;
                        }

                        private List<Resource> allResources() {
                            return Resource.sortByName(resourcesDAO
                                    .list(Resource.class));
                        }
                    }, new IReattacher<Resource>() {

                        @Override
                        public Resource reattach(Resource entity) {
                            return resourcesDAO.findExistingEntity(entity
                                    .getId());
                        }
                    });
        }

        private Map<Resource, List<ResourceAllocation<?>>> eachWithAllocations(
                List<Resource> allResources) {
            Map<Resource, List<ResourceAllocation<?>>> result = new LinkedHashMap<Resource, List<ResourceAllocation<?>>>();
            for (Resource resource : allResources) {
                IAllocationCriteria criteria = and(onInterval(),
                        relatedToResource(resource));
                result.put(resource, ResourceAllocation
                        .sortedByStartDate(doReplacementsIfNeeded(
                                resourceAllocationDAO.findAllocationsRelatedTo(
                                        getCurrentScenario(), resource,
                                        parameters.getInitDateFilter(),
                                        parameters.getEndDateFilter()),
                                criteria)));
            }
            return result;
        }

        private IAllocationCriteria relatedToResource(Resource resource) {
            return new RelatedWithResource(resource);
        }

    }

    private class ByCriterionFinder extends
            ResourceAllocationsFinder<Criterion> {

        private final Map<Criterion, List<ResourceAllocation<?>>> allocationsByCriterion;
        private final Paginator<Criterion> criterions;

        public ByCriterionFinder(ResourceLoadParameters parameters) {
            super(parameters);
            this.criterions = findCriterions();
            this.allocationsByCriterion = allocationsByCriterion(this.criterions
                    .getForCurrentPage());
        }

        @Override
        Paginator<Criterion> getPaginator() {
            return criterions;
        }

        @Override
        List<Resource> getResourcesIncluded() {
            Set<Resource> result = new HashSet<Resource>();
            for (List<ResourceAllocation<?>> each : getFoundAllocations()
                    .values()) {
                for (ResourceAllocation<?> eachAllocation : each) {
                    result.addAll(eachAllocation.getAssociatedResources());
                }
            }
            return new ArrayList<Resource>(result);
        }

        @Override
        protected Map<Criterion, List<ResourceAllocation<?>>> getFoundAllocations() {
            return allocationsByCriterion;
        }

        @Override
        List<LoadTimeLine> buildTimeLines() {
            return new ByCriterionLoadTimesLinesBuilder(parameters)
                    .buildGroupsByCriterion(getFoundAllocations());
        }

        private Paginator<Criterion> findCriterions() {
            return parameters.getEntities(Criterion.class,
                    criterionRetriever(), new IReattacher<Criterion>() {

                        @Override
                        public Criterion reattach(Criterion entity) {
                            criterionDAO.reattachUnmodifiedEntity(entity);
                            return entity;
                        }
                    });
        }

        Callable<List<Criterion>> criterionRetriever() {
            return new Callable<List<Criterion>>() {

                @Override
                public List<Criterion> call() throws Exception {
                    return Criterion
                            .sortByInclusionTypeAndName(findCriterions());
                }

                private Collection<Criterion> findCriterions() {
                    if (parameters.thereIsCurrentOrder()) {
                        List<Task> tasks = justTasks(parameters
                                .getCurrentOrder()
                                .getAllChildrenAssociatedTaskElements());
                        return getCriterionsOn(tasks);
                    } else {
                        return findAllocationsGroupedByCriteria().keySet();
                    }
                }

                private List<Criterion> getCriterionsOn(
                        Collection<? extends Task> tasks) {
                    Set<Criterion> result = new LinkedHashSet<Criterion>();
                    for (Task eachTask : tasks) {
                        result.addAll(getCriterionsOn(eachTask));
                    }
                    return new ArrayList<Criterion>(result);
                }

                private Set<Criterion> getCriterionsOn(Task task) {
                    Set<Criterion> result = new LinkedHashSet<Criterion>();
                    for (GenericResourceAllocation eachAllocation : onlyGeneric(task
                            .getSatisfiedResourceAllocations())) {
                        result.addAll(eachAllocation.getCriterions());
                    }
                    return result;
                }
            };
        }

        private Map<Criterion, List<ResourceAllocation<?>>> allocationsByCriterion(
                List<Criterion> criterions) {
            return withAssociatedSpecific(findAllocationsGroupedByCriteria(
                    scenarioManager.getCurrent(), criterions));
        }

        private Map<Criterion, List<ResourceAllocation<?>>> findAllocationsGroupedByCriteria(
                Scenario onScenario, List<Criterion> relatedWith) {
            Map<Criterion, List<ResourceAllocation<?>>> result = new LinkedHashMap<Criterion, List<ResourceAllocation<?>>>();
            for (Criterion criterion : relatedWith) {
                IAllocationCriteria criteria = and(onInterval(),
                        new RelatedWith(criterion));
                result.put(
                        criterion,
                        ResourceAllocation.sortedByStartDate(doReplacementsIfNeeded(
                                resourceAllocationDAO
                                        .findGenericAllocationsRelatedToCriterion(
                                                getCurrentScenario(),
                                                criterion, asDate(parameters
                                                        .getInitDateFilter()),
                                                asDate(parameters
                                                        .getEndDateFilter())),
                                criteria)));

            }
            return result;
        }

        private Map<Criterion, List<ResourceAllocation<?>>> withAssociatedSpecific(
                Map<Criterion, List<ResourceAllocation<?>>> genericAllocationsByCriterion) {
            Map<Criterion, List<ResourceAllocation<?>>> result = new HashMap<Criterion, List<ResourceAllocation<?>>>();
            for (Entry<Criterion, List<ResourceAllocation<?>>> each : genericAllocationsByCriterion
                    .entrySet()) {
                List<ResourceAllocation<?>> both = new ArrayList<ResourceAllocation<?>>();
                both.addAll(each.getValue());
                both.addAll(doReplacementsIfNeeded(resourceAllocationDAO
                        .findSpecificAllocationsRelatedTo(getCurrentScenario(),
                                each.getKey(),
                                asDate(parameters.getInitDateFilter()),
                                asDate(parameters.getEndDateFilter())),
                        and(onInterval(), specificRelatedTo(each.getKey()))));
                result.put(each.getKey(), both);
            }
            return result;
        }

        private IAllocationCriteria specificRelatedTo(Criterion key) {
            return new SpecificRelatedWithCriterionOnInterval(key,
                    parameters.getInitDateFilter(),
                    parameters.getEndDateFilter());
        }

        private Map<Criterion, List<GenericResourceAllocation>> findAllocationsGroupedByCriteria() {
            return doReplacementsIfNeeded(
                    resourceAllocationDAO.findGenericAllocationsByCriterion(
                            getCurrentScenario(),
                            asDate(parameters.getInitDateFilter()),
                            asDate(parameters.getEndDateFilter())));
        }

        private Map<Criterion, List<GenericResourceAllocation>> doReplacementsIfNeeded(
                Map<Criterion, List<GenericResourceAllocation>> map) {
            if (!parameters.thereIsCurrentOrder()) {
                return map;
            }
            Map<Criterion, List<GenericResourceAllocation>> result = new HashMap<Criterion, List<GenericResourceAllocation>>();
            for (Entry<Criterion, List<GenericResourceAllocation>> each : map
                    .entrySet()) {
                IAllocationCriteria criteria = and(onInterval(),
                        new RelatedWith(each.getKey()));
                List<ResourceAllocation<?>> replaced = parameters
                        .getPlanningState().replaceByCurrentOnes(
                                each.getValue(), criteria);
                if (!replaced.isEmpty()) {
                    result.put(each.getKey(), ResourceAllocation.getOfType(
                            GenericResourceAllocation.class, replaced));
                }
            }
            return result;
        }

    }

    class LoadTimeLinesBuilder {

        final PeriodBuilderFactory periodBuilderFactory;
        private final ResourceLoadParameters parameters;

        public LoadTimeLinesBuilder(ResourceLoadParameters parameters) {
            this.parameters = parameters;
            this.periodBuilderFactory = new PeriodBuilderFactory(
                    parameters.getInitDateFilter(),
                    parameters.getEndDateFilter());
        }

        TimeLineRole<BaseEntity> getCurrentTimeLineRole(BaseEntity entity) {
            return new TimeLineRole<BaseEntity>(entity);
        }

        LoadTimeLine buildGroupFor(Resource resource,
                List<? extends ResourceAllocation<?>> sortedByStartDate) {
            TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(resource);
            LoadTimeLine result = new LoadTimeLine(buildTimeLine(resource,
                    resource.getName(), sortedByStartDate, "resource", role),
                    buildSecondLevel(resource, sortedByStartDate));
            return result;
        }

        private List<LoadTimeLine> buildSecondLevel(Resource resource,
                List<? extends ResourceAllocation<?>> sortedByStartDate) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            Map<Order, List<ResourceAllocation<?>>> byOrder = byOrder(sortedByStartDate);

            if (thereIsCurrentOrder()) {
                List<ResourceAllocation<?>> forCurrentOrder = byOrder
                        .get(getCurrentOrder());
                if (forCurrentOrder != null) {
                    result.addAll(buildTimeLinesForOrder(resource,
                            forCurrentOrder));
                }
                byOrder.remove(getCurrentOrder());
                // build time lines for other orders
                LoadTimeLine lineOthersOrders = buildTimeLinesForOtherOrders(
                        resource, byOrder);
                if (lineOthersOrders != null) {
                    result.add(lineOthersOrders);
                }
            } else {
                result.addAll(buildTimeLinesGroupForOrder(resource, byOrder));
            }
            return result;
        }

        Order getCurrentOrder() {
            return parameters.getCurrentOrder();
        }

        boolean thereIsCurrentOrder() {
            return parameters.thereIsCurrentOrder();
        }

        private List<LoadTimeLine> buildTimeLinesForOrder(Resource resource,
                List<ResourceAllocation<?>> sortedByStartDate) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            result.addAll(buildTimeLinesForEachTask(resource,
                    onlySpecific(sortedByStartDate)));
            result.addAll(buildTimeLinesForEachCriterion(resource,
                    onlyGeneric(sortedByStartDate)));
            Collections.sort(result, LoadTimeLine.byStartAndEndDate());
            return result;
        }

        private List<LoadTimeLine> buildTimeLinesForEachTask(Resource resource,
                List<SpecificResourceAllocation> sortedByStartDate) {

            List<ResourceAllocation<?>> listOnlySpecific = new ArrayList<ResourceAllocation<?>>(
                    sortedByStartDate);
            Map<Task, List<ResourceAllocation<?>>> byTask = ResourceAllocation
                    .byTask(listOnlySpecific);

            List<LoadTimeLine> secondLevel = new ArrayList<LoadTimeLine>();
            for (Entry<Task, List<ResourceAllocation<?>>> entry : byTask
                    .entrySet()) {
                Task task = entry.getKey();
                TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(task);
                LoadTimeLine timeLine = buildTimeLine(resource, task.getName(),
                        entry.getValue(), "specific", role);
                if (!timeLine.isEmpty()) {
                    secondLevel.add(timeLine);
                }

            }
            return secondLevel;
        }

        private List<LoadTimeLine> buildTimeLinesForEachCriterion(
                Resource resource,
                List<GenericResourceAllocation> sortdByStartDate) {
            Map<Set<Criterion>, List<GenericResourceAllocation>> byCriterions = GenericResourceAllocation
                    .byCriterions(sortdByStartDate);

            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Entry<Set<Criterion>, List<GenericResourceAllocation>> entry : byCriterions
                    .entrySet()) {

                Map<Task, List<ResourceAllocation<?>>> byTask = ResourceAllocation
                        .byTask(new ArrayList<ResourceAllocation<?>>(entry
                                .getValue()));

                for (Entry<Task, List<ResourceAllocation<?>>> entryTask : byTask
                        .entrySet()) {

                    Task task = entryTask.getKey();
                    List<GenericResourceAllocation> resouceAllocations = onlyGeneric(entryTask
                            .getValue());
                    TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(task);
                    LoadTimeLine timeLine = buildTimeLine(entry.getKey(), task,
                            resource, "generic", resouceAllocations, role);
                    if (!timeLine.isEmpty()) {
                        result.add(timeLine);
                    }

                }
            }
            return result;
        }

        private LoadTimeLine buildTimeLine(Collection<Criterion> criterions,
                Task task, Resource resource, String type,
                List<GenericResourceAllocation> allocationsSortedByStartDate,
                TimeLineRole<BaseEntity> role) {
            LoadPeriodGeneratorFactory periodGeneratorFactory = LoadPeriodGenerator
                    .onResourceSatisfying(resource, criterions);
            List<LoadPeriod> loadPeriods = periodBuilderFactory.build(
                    periodGeneratorFactory, allocationsSortedByStartDate);
            return new LoadTimeLine(getName(criterions, task), loadPeriods,
                    type, role);
        }

        String getName(Collection<? extends Criterion> criterions, Task task) {
            String prefix = task.getName();
            return (prefix + " :: " + Criterion.getCaptionFor(criterions));
        }

        private LoadTimeLine buildTimeLinesForOtherOrders(Resource resource,
                Map<Order, List<ResourceAllocation<?>>> byOrder) {
            List<ResourceAllocation<?>> resourceAllocations = getAllSortedValues(byOrder);
            if (resourceAllocations.isEmpty()) {
                return null;
            }
            TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(null);
            LoadTimeLine group = new LoadTimeLine(
                    buildTimeLine(resource, _("Other projects"),
                            resourceAllocations, "resource", role),
                    buildTimeLinesGroupForOrder(resource, byOrder));
            return group;
        }

        List<ResourceAllocation<?>> getAllSortedValues(
                Map<Order, List<ResourceAllocation<?>>> byOrder) {
            List<ResourceAllocation<?>> resourceAllocations = new ArrayList<ResourceAllocation<?>>();
            for (List<ResourceAllocation<?>> listAllocations : byOrder.values()) {
                resourceAllocations.addAll(listAllocations);
            }
            return ResourceAllocation.sortedByStartDate(resourceAllocations);
        }

        private List<LoadTimeLine> buildTimeLinesGroupForOrder(
                Resource resource,
                Map<Order, List<ResourceAllocation<?>>> byOrder) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Order order : byOrder.keySet()) {
                TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(order);
                result.add(new LoadTimeLine(buildTimeLine(resource,
                        order.getName(), byOrder.get(order), "resource", role),
                        buildTimeLinesForOrder(resource, byOrder.get(order))));
            }
            Collections.sort(result, LoadTimeLine.byStartAndEndDate());
            return result;
        }

        LoadTimeLine buildTimeLine(Resource resource, String name,
                List<? extends ResourceAllocation<?>> sortedByStartDate,
                String type, TimeLineRole<BaseEntity> role) {

            List<LoadPeriod> loadPeriods = periodBuilderFactory
                    .build(LoadPeriodGenerator.onResource(resource),
                            sortedByStartDate);
            return new LoadTimeLine(name, loadPeriods, type, role);
        }

    }

    class ByResourceLoadTimesLinesBuilder extends LoadTimeLinesBuilder {

        public ByResourceLoadTimesLinesBuilder(ResourceLoadParameters parameters) {
            super(parameters);
        }

        List<LoadTimeLine> buildGroupsByResource(
                Map<Resource, List<ResourceAllocation<?>>> map) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Entry<Resource, List<ResourceAllocation<?>>> each : map
                    .entrySet()) {
                LoadTimeLine l = buildGroupFor(each.getKey(), each.getValue());
                result.add(l);
            }
            return result;
        }

    }

    class ByCriterionLoadTimesLinesBuilder extends LoadTimeLinesBuilder {

        public ByCriterionLoadTimesLinesBuilder(
                ResourceLoadParameters parameters) {
            super(parameters);
        }

        List<LoadTimeLine> buildGroupsByCriterion(
                Map<Criterion, List<ResourceAllocation<?>>> map) {
            return groupsFor(map);
        }

        private List<LoadTimeLine> groupsFor(
                Map<Criterion, List<ResourceAllocation<?>>> allocationsByCriterion) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Entry<Criterion, List<ResourceAllocation<?>>> each : allocationsByCriterion
                    .entrySet()) {
                Criterion criterion = each.getKey();
                List<ResourceAllocation<?>> allocations = ResourceAllocation
                        .sortedByStartDate(each.getValue());
                if (allocations == null) {
                    continue;
                }
                TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(criterion);
                LoadTimeLine group = new LoadTimeLine(createMain(criterion,
                        allocations, role), buildSecondaryLevels(criterion,
                        allocations));
                if (!group.isEmpty()) {
                    result.add(group);
                }
            }
            return result;
        }

        private LoadTimeLine createMain(Criterion criterion,
                List<? extends ResourceAllocation<?>> orderedAllocations,
                TimeLineRole<BaseEntity> role) {
            return new LoadTimeLine(criterion.getType().getName() + ": "
                    + criterion.getName(), createPeriods(criterion,
                    orderedAllocations), "global-generic", role);
        }

        private List<LoadPeriod> createPeriods(Criterion criterion,
                List<? extends ResourceAllocation<?>> value) {
            return periodBuilderFactory.build(LoadPeriodGenerator.onCriterion(
                    criterion, resourcesSearchModel), value);
        }

        private List<LoadTimeLine> buildSecondaryLevels(Criterion criterion,
                List<? extends ResourceAllocation<?>> allocations) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            result.addAll(buildSubLevels(criterion, ResourceAllocation
                    .getOfType(GenericResourceAllocation.class, allocations)));
            result.add(buildRelatedSpecificAllocations(criterion, allocations));
            Collections.sort(result, LoadTimeLine.byStartAndEndDate());
            return result;
        }

        private List<LoadTimeLine> buildSubLevels(Criterion criterion,
                List<? extends ResourceAllocation<?>> allocations) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            Map<Order, List<ResourceAllocation<?>>> byOrder = byOrder(new ArrayList<ResourceAllocation<?>>(
                    allocations));

            if (thereIsCurrentOrder()) {
                List<ResourceAllocation<?>> allocationsForCurrent = byOrder
                        .get(getCurrentOrder());
                if (allocationsForCurrent != null) {
                    result.addAll(buildTimeLinesForOrder(getCurrentOrder(),
                            criterion, allocationsForCurrent));
                }
                byOrder.remove(getCurrentOrder());
                // build time lines for other orders
                LoadTimeLine lineOthersOrders = buildTimeLinesForOtherOrders(
                        criterion, byOrder);
                if (lineOthersOrders != null) {
                    result.add(lineOthersOrders);
                }
            } else {
                result.addAll(buildTimeLinesGroupForOrder(criterion, byOrder));
            }
            return result;
        }

        private LoadTimeLine buildTimeLinesForOtherOrders(Criterion criterion,
                Map<Order, List<ResourceAllocation<?>>> byOrder) {
            List<ResourceAllocation<?>> allocations = getAllSortedValues(byOrder);
            if (allocations.isEmpty()) {
                return null;
            }

            LoadTimeLine group = new LoadTimeLine(buildTimeLine(criterion,
                    "Other projects", "global-generic", allocations,
                    getCurrentTimeLineRole(null)), buildTimeLinesGroupForOrder(
                    criterion, byOrder));
            return group;
        }

        private List<LoadTimeLine> buildTimeLinesGroupForOrder(
                Criterion criterion,
                Map<Order, List<ResourceAllocation<?>>> byOrder) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Order order : byOrder.keySet()) {
                if (byOrder.get(order) == null) {
                    // no allocations found for order
                    continue;
                }
                TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(order);
                result.add(new LoadTimeLine(buildTimeLine(criterion,
                        order.getName(), "global-generic", byOrder.get(order),
                        role), buildTimeLinesForOrder(order, criterion,
                        byOrder.get(order))));
            }
            return result;
        }

        LoadTimeLine buildTimeLine(Criterion criterion, String name,
                String type, List<ResourceAllocation<?>> allocations,
                TimeLineRole<BaseEntity> role) {
            List<GenericResourceAllocation> generics = onlyGeneric(allocations);
            return new LoadTimeLine(name, createPeriods(criterion, generics),
                    type, role);
        }

        private List<LoadTimeLine> buildTimeLinesForOrder(Order order,
                Criterion criterion, List<ResourceAllocation<?>> allocations) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            result.addAll(buildTimeLinesForEachTask(criterion,
                    onlyGeneric(allocations)));
            result.addAll(buildTimeLinesForEachResource(criterion,
                    onlySpecific(allocations), getCurrentTimeLineRole(order)));
            Collections.sort(result, LoadTimeLine.byStartAndEndDate());
            return result;
        }

        private List<LoadTimeLine> buildTimeLinesForEachTask(
                Criterion criterion, List<GenericResourceAllocation> allocations) {
            Map<Task, List<GenericResourceAllocation>> byTask = ResourceAllocation
                    .byTask(allocations);

            List<LoadTimeLine> secondLevel = new ArrayList<LoadTimeLine>();
            for (Entry<Task, List<GenericResourceAllocation>> entry : byTask
                    .entrySet()) {
                Task task = entry.getKey();

                Map<Set<Criterion>, List<GenericResourceAllocation>> mapSameCriteria = getAllocationsWithSameCriteria((entry
                        .getValue()));
                for (Entry<Set<Criterion>, List<GenericResourceAllocation>> entrySameCriteria : mapSameCriteria
                        .entrySet()) {
                    Set<Criterion> criterions = entrySameCriteria.getKey();
                    List<GenericResourceAllocation> genericAllocations = entrySameCriteria
                            .getValue();
                    List<ResourceAllocation<?>> resourceAllocations = new ArrayList<ResourceAllocation<?>>(
                            genericAllocations);
                    TimeLineRole<BaseEntity> role = getCurrentTimeLineRole(task);

                    /**
                     * Each resource line has the same role than its allocated
                     * task, so that link with the resource allocation screen
                     */
                    LoadTimeLine timeLine = new LoadTimeLine(buildTimeLine(
                            criterions, task, criterion, "global-generic",
                            resourceAllocations, role),
                            buildTimeLinesForEachResource(criterion,
                                    genericAllocations, role));
                    if (!timeLine.isEmpty()) {
                        secondLevel.add(timeLine);
                    }
                }
            }
            return secondLevel;
        }

        private List<LoadTimeLine> buildTimeLinesForEachResource(
                Criterion criterion,
                List<? extends ResourceAllocation<?>> allocations,
                TimeLineRole<BaseEntity> role) {
            Map<Resource, List<ResourceAllocation<?>>> byResource = ResourceAllocation
                    .byResource(allocations);

            List<LoadTimeLine> secondLevel = new ArrayList<LoadTimeLine>();
            for (Entry<Resource, List<ResourceAllocation<?>>> entry : byResource
                    .entrySet()) {
                Resource resource = entry.getKey();
                List<ResourceAllocation<?>> resourceAllocations = entry
                        .getValue();
                String descriptionTimeLine = resource.getShortDescription();

                LoadTimeLine timeLine = buildTimeLine(resource,
                        descriptionTimeLine, resourceAllocations, "generic",
                        role);
                if (!timeLine.isEmpty()) {
                    secondLevel.add(timeLine);
                }

            }
            return secondLevel;
        }

        private LoadTimeLine buildTimeLine(Collection<Criterion> criterions,
                Task task, Criterion criterion, String type,
                List<ResourceAllocation<?>> allocations,
                TimeLineRole<BaseEntity> role) {
            return buildTimeLine(criterion, getName(criterions, task), type,
                    allocations, role);
        }

        private LoadTimeLine buildRelatedSpecificAllocations(
                Criterion criterion,
                List<? extends ResourceAllocation<?>> allocations) {
            List<SpecificResourceAllocation> specific = ResourceAllocation
                    .getOfType(SpecificResourceAllocation.class, allocations);

            LoadTimeLine main = new LoadTimeLine(_("Specific Allocations"),
                    createPeriods(criterion, specific), "related-specific",
                    getCurrentTimeLineRole(criterion));
            List<LoadTimeLine> children = buildGroupsFor(ResourceAllocation
                    .byResource(new ArrayList<ResourceAllocation<?>>(specific)));
            return new LoadTimeLine(main, children);
        }

        private List<LoadTimeLine> buildGroupsFor(
                Map<Resource, List<ResourceAllocation<?>>> map) {
            List<LoadTimeLine> result = new ArrayList<LoadTimeLine>();
            for (Entry<Resource, List<ResourceAllocation<?>>> each : map
                    .entrySet()) {
                LoadTimeLine l = buildGroupFor(each.getKey(), each.getValue());
                if (!l.isEmpty()) {
                    result.add(l);
                }
            }
            return result;
        }

    }


    public static Date asDate(LocalDate date) {
        if (date == null) {
            return null;
        }
        return date.toDateTimeAtStartOfDay().toDate();
    }

    public static LocalDate toLocal(Date date) {
        if (date == null) {
            return null;
        }
        return LocalDate.fromDateFields(date);
    }

    private Map<Set<Criterion>, List<GenericResourceAllocation>> getAllocationsWithSameCriteria(
            List<GenericResourceAllocation> genericAllocations) {
        return GenericResourceAllocation.byCriterions(genericAllocations);
    }

    private Map<Order, List<ResourceAllocation<?>>> byOrder(
            Collection<? extends ResourceAllocation<?>> allocations) {
        Map<Order, List<ResourceAllocation<?>>> result = new HashMap<Order, List<ResourceAllocation<?>>>();
        for (ResourceAllocation<?> resourceAllocation : allocations) {
            if ((resourceAllocation.isSatisfied())
                    && (resourceAllocation.getTask() != null)) {
                OrderElement orderElement = resourceAllocation.getTask()
                        .getOrderElement();
                Order order = orderDAO.loadOrderAvoidingProxyFor(orderElement);
                initializeIfNeeded(result, order);
                result.get(order).add(resourceAllocation);
            }
        }
        return result;
    }

    private void initializeIfNeeded(
            Map<Order, List<ResourceAllocation<?>>> result, Order order) {
        if (!result.containsKey(order)) {
            result.put(order, new ArrayList<ResourceAllocation<?>>());
        }
    }

    private static List<GenericResourceAllocation> onlyGeneric(
            Collection<? extends ResourceAllocation<?>> sortedByStartDate) {
        return ResourceAllocation.getOfType(GenericResourceAllocation.class,
                sortedByStartDate);
    }

    private static List<SpecificResourceAllocation> onlySpecific(
            List<ResourceAllocation<?>> sortedByStartDate) {
        return ResourceAllocation.getOfType(SpecificResourceAllocation.class,
                sortedByStartDate);
    }

    @Override
    @Transactional(readOnly = true)
    public boolean isExpandResourceLoadViewCharts() {

        User user;
        try {
            user = this.userDAO.findByLoginName(SecurityUtils
                    .getSessionUserLoginName());
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException(e);
        }
        return user.isExpandResourceLoadViewCharts();
    }

    @Override
    @Transactional(readOnly = true)
    public User getUser() {
        User user;
        try {
            user = this.userDAO.findByLoginName(SecurityUtils
                    .getSessionUserLoginName());
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException(e);
        }
        // Attach filter bandbox elements
        if (user.getResourcesLoadFilterCriterion() != null) {
            user.getResourcesLoadFilterCriterion().getFinderPattern();
        }
        return user;
    }

}

class PeriodBuilderFactory {
    private final LocalDate initDateFilter;
    private final LocalDate endDateFilter;

    public PeriodBuilderFactory(LocalDate initDateFilter, LocalDate endDateFilter) {
        this.initDateFilter = initDateFilter;
        this.endDateFilter = endDateFilter;
    }

    public List<LoadPeriod> build(LoadPeriodGeneratorFactory factory, List<? extends ResourceAllocation<?>> sortedByStartDate){
        if (initDateFilter == null && endDateFilter == null) {
            return PeriodsBuilder.build(factory, sortedByStartDate);
        } else {
            return PeriodsBuilder.build(factory, sortedByStartDate, asDate(initDateFilter), asDate(endDateFilter));
        }
    }

    private Date asDate(LocalDate date) {
        return ResourceLoadModel.asDate(date);
    }

}

class PeriodsBuilder {

    private final List<? extends ResourceAllocation<?>> sortedByStartDate;

    private final List<LoadPeriodGenerator> loadPeriodsGenerators = new LinkedList<LoadPeriodGenerator>();

    private final LoadPeriodGeneratorFactory factory;

    private PeriodsBuilder(LoadPeriodGeneratorFactory factory,
            List<? extends ResourceAllocation<?>> sortedByStartDate) {
        this.factory = factory;
        this.sortedByStartDate = sortedByStartDate;
    }

    public static List<LoadPeriod> build(LoadPeriodGeneratorFactory factory,
            List<? extends ResourceAllocation<?>> sortedByStartDate) {
        return new PeriodsBuilder(factory, sortedByStartDate).buildPeriods();
    }

    public static List<LoadPeriod> build(LoadPeriodGeneratorFactory factory,
            List<? extends ResourceAllocation<?>> sortedByStartDate,
            Date startDateFilter, Date endDateFilter) {
        List<LoadPeriod> list = new PeriodsBuilder(factory, sortedByStartDate).buildPeriods();
        List<LoadPeriod> toReturn = new ArrayList<LoadPeriod>();
        for (LoadPeriod loadPeriod : list) {

            final GanttDate finalStartDate;
            if (startDateFilter != null) {
                finalStartDate = GanttDate.max(GanttDate
                        .createFrom(new LocalDate(startDateFilter.getTime())),
                        loadPeriod.getStart());
            } else {
                finalStartDate = loadPeriod.getStart();
            }

            final GanttDate finalEndDate;
            if (endDateFilter != null) {
                finalEndDate = GanttDate.min(loadPeriod.getEnd(), GanttDate
                        .createFrom(new LocalDate(endDateFilter.getTime())));
            } else {
                finalEndDate = loadPeriod.getEnd();
            }
            if (finalStartDate.compareTo(finalEndDate) < 0) {
                toReturn.add(new LoadPeriod(finalStartDate, finalEndDate,
                        loadPeriod.getAvailableEffort(),
                        loadPeriod.getAssignedEffort(), loadPeriod.getLoadLevel()));
            }
        }
        return toReturn;
    }

    private List<LoadPeriod> buildPeriods() {
        for (ResourceAllocation<?> resourceAllocation : sortedByStartDate) {
            loadPeriodsGenerators.add(factory.create(resourceAllocation));
        }
        joinPeriodGenerators();
        return toGenerators(loadPeriodsGenerators);
    }

    private List<LoadPeriod> toGenerators(List<LoadPeriodGenerator> generators) {
        List<LoadPeriod> result = new ArrayList<LoadPeriod>();
        for (LoadPeriodGenerator loadPeriodGenerator : generators) {
            LoadPeriod period = loadPeriodGenerator.build();
            if (period != null) {
                result.add(period);
            }
        }
        return result;
    }

    private void joinPeriodGenerators() {
        ListIterator<LoadPeriodGenerator> iterator = loadPeriodsGenerators
                .listIterator();
        while (iterator.hasNext()) {
            final LoadPeriodGenerator current = findNextOneOverlapping(iterator);
            if (current != null) {
                rewind(iterator, current);
                iterator.remove();
                LoadPeriodGenerator next = iterator.next();
                iterator.remove();
                List<LoadPeriodGenerator> generated = current.join(next);
                final LoadPeriodGenerator positionToComeBack = generated.get(0);
                final List<LoadPeriodGenerator> remaining = loadPeriodsGenerators
                        .subList(iterator.nextIndex(), loadPeriodsGenerators
                                .size());
                List<LoadPeriodGenerator> generatorsSortedByStartDate = mergeListsKeepingByStartSortOrder(
                        generated, remaining);
                final int takenFromRemaining = generatorsSortedByStartDate
                        .size()
                        - generated.size();
                removeNextElements(iterator, takenFromRemaining);
                addAtCurrentPosition(iterator, generatorsSortedByStartDate);
                rewind(iterator, positionToComeBack);
            }
        }
    }

    private LoadPeriodGenerator findNextOneOverlapping(
            ListIterator<LoadPeriodGenerator> iterator) {
        while (iterator.hasNext()) {
            LoadPeriodGenerator current = iterator.next();
            if (!iterator.hasNext()) {
                return null;
            }
            LoadPeriodGenerator next = peekNext(iterator);
            if (current.overlaps(next)) {
                return current;
            }
        }
        return null;
    }

    private void addAtCurrentPosition(
            ListIterator<LoadPeriodGenerator> iterator,
            List<LoadPeriodGenerator> sortedByStartDate) {
        for (LoadPeriodGenerator l : sortedByStartDate) {
            iterator.add(l);
        }
    }

    private void removeNextElements(ListIterator<LoadPeriodGenerator> iterator,
            final int elementsNumber) {
        for (int i = 0; i < elementsNumber; i++) {
            iterator.next();
            iterator.remove();
        }
    }

    private void rewind(ListIterator<LoadPeriodGenerator> iterator,
            LoadPeriodGenerator nextOne) {
        while (peekNext(iterator) != nextOne) {
            iterator.previous();
        }
    }

    private List<LoadPeriodGenerator> mergeListsKeepingByStartSortOrder(
            List<LoadPeriodGenerator> joined,
            List<LoadPeriodGenerator> remaining) {
        List<LoadPeriodGenerator> result = new ArrayList<LoadPeriodGenerator>();
        ListIterator<LoadPeriodGenerator> joinedIterator = joined
                .listIterator();
        ListIterator<LoadPeriodGenerator> remainingIterator = remaining
                .listIterator();
        while (joinedIterator.hasNext() && remainingIterator.hasNext()) {
            LoadPeriodGenerator fromJoined = peekNext(joinedIterator);
            LoadPeriodGenerator fromRemaining = peekNext(remainingIterator);
            if (fromJoined.getStart().compareTo(fromRemaining.getStart()) <= 0) {
                result.add(fromJoined);
                joinedIterator.next();
            } else {
                result.add(fromRemaining);
                remainingIterator.next();
            }
        }
        if (joinedIterator.hasNext()) {
            result.addAll(joined.subList(joinedIterator.nextIndex(), joined
                    .size()));
        }
        return result;
    }

    private LoadPeriodGenerator peekNext(
            ListIterator<LoadPeriodGenerator> iterator) {
        if (!iterator.hasNext()) {
            return null;
        }
        LoadPeriodGenerator result = iterator.next();
        iterator.previous();
        return result;
    }

}
TOP

Related Classes of org.libreplan.web.resourceload.ResourceLoadModel$ByCriterionFinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.