org.libreplan.importers.OrderImporterMPXJ.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.importers.OrderImporterMPXJ.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2012 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.importers;

import static org.libreplan.web.I18nHelper._;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.UUID;

import net.sf.mpxj.reader.ProjectReader;
import net.sf.mpxj.reader.ProjectReaderUtility;

import org.apache.commons.lang3.Validate;
import org.joda.time.LocalDate;
import org.libreplan.business.calendars.daos.IBaseCalendarDAO;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.common.daos.IEntitySequenceDAO;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.exceptions.ValidationException;
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.orders.entities.OrderLine;
import org.libreplan.business.orders.entities.OrderLineGroup;
import org.libreplan.business.orders.entities.TaskSource;
import org.libreplan.business.planner.daos.IDependencyDAO;
import org.libreplan.business.planner.daos.ITaskElementDAO;
import org.libreplan.business.planner.daos.ITaskSourceDAO;
import org.libreplan.business.planner.entities.Dependency;
import org.libreplan.business.planner.entities.Dependency.Type;
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.scenarios.IScenarioManager;
import org.libreplan.business.scenarios.entities.OrderVersion;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.workingday.IntraDayDate;
import org.libreplan.importers.DependencyDTO.TypeOfDependencyDTO;
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;

/**
 *
 * Has all the methods needed to successfully import some external project files into Libreplan using MPXJ.
 *
 * @author Alba Carro Prez <alba.carro@gmail.com>
 * @author Vova Perebykivskyi <vova@libreplan-enterprise.com>>
 */
@Component
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class OrderImporterMPXJ implements IOrderImporter {

    @Autowired
    private IBaseCalendarDAO baseCalendarDAO;

    @Autowired
    private IEntitySequenceDAO entitySequenceDAO;

    @Autowired
    protected IAdHocTransactionService transactionService;

    @Autowired
    private IConfigurationDAO configurationDAO;

    @Autowired
    private IOrderDAO orderDAO;

    @Autowired
    private IDependencyDAO dependencyDAO;

    @Autowired
    private ITaskElementDAO taskDAO;

    @Autowired
    private ITaskSourceDAO taskSourceDAO;

    @Autowired
    private IScenarioManager scenarioManager;

    /**
     * Makes a {@link OrderDTO} from a InputStream.
     *
     * Uses the filename in order to get the specific ProjectReader for each kind of file (.mpp, .planner, etc).
     *
     * @param file
     *            InputStream to extract data from.
     * @param filename
     *            String with the name of the original file of the InputStream.
     * @return ImportData with the data that we want to import.
     */
    @Override
    public OrderDTO getImportData(InputStream file, String filename) {
        try {

            ProjectReader reader = ProjectReaderUtility.getProjectReader(filename);

            return MPXJProjectFileConverter.convert(reader.read(file), filename);

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

    private String getCode(EntityNameEnum entity) {

        String code = entitySequenceDAO.getNextEntityCode(entity);

        if (code == null) {
            throw new ConcurrentModificationException("Could not retrieve Code. Please, try again later");
        }

        return code;
    }

    /**
     * Makes a {@link Order} from a {@link OrderDTO}.
     *
     * @param project
     *            ImportData to extract data from.
     * @return Order with all the data that we want.
     */
    @Override
    @Transactional(readOnly = true)
    public Order convertImportDataToOrder(OrderDTO project, boolean importCalendar) {

        String code = getCode(EntityNameEnum.ORDER);

        Scenario current = scenarioManager.getCurrent();

        OrderVersion orderVersion = OrderVersion.createInitialVersion(current);

        Validate.notNull(orderVersion);

        OrderElement orderElement;

        orderElement = Order.createUnvalidated(code);

        orderElement.setCodeAutogenerated(true);

        ((Order) orderElement).setVersionForScenario(current, orderVersion);

        ((Order) orderElement).setDependenciesConstraintsHavePriority(true);

        BaseCalendar calendar = configurationDAO.getConfiguration().getDefaultCalendar();

        if (importCalendar & project.calendarName != null) {
            ((Order) orderElement).setCalendar(findBaseCalendar(project.calendarName));
        } else {
            ((Order) orderElement).setCalendar(calendar);
        }

        orderElement.useSchedulingDataFor(orderVersion);

        List<OrderElement> children = new ArrayList<>();

        for (OrderElementDTO task : project.tasks) {
            children.add(convertImportTaskToOrderElement(orderVersion, task));
        }

        for (OrderElement child : children) {
            ((OrderLineGroup) orderElement).add(child);
        }

        orderElement.setName(project.name + ": " + project.hashCode());
        orderElement.setCode(code);

        orderElement.setInitDate(project.startDate);

        orderElement.setDeadline(project.deadline);

        ((Order) orderElement).calculateAndSetTotalHours();

        project.order = (Order) orderElement;

        ((Order) orderElement)
                .generateOrderElementCodes(entitySequenceDAO.getNumberOfDigitsCode(EntityNameEnum.ORDER));

        return (Order) orderElement;

    }

    /**
     * Private method.
     *
     * It makes a {@link OrderElement} from a {@link OrderElementDTO}
     *
     * @param task
     *            ImportTask to extract data from.
     * @param orderVersion
     *            Number of version.
     * @return OrderElement OrderElement that represent the data.
     */
    private OrderElement convertImportTaskToOrderElement(OrderVersion orderVersion, OrderElementDTO task) {

        Validate.notNull(orderVersion);
        OrderElement orderElement;

        if (task.children.size() == 0) {
            orderElement = OrderLine.createUnvalidatedWithUnfixedPercentage(UUID.randomUUID().toString(),
                    task.totalHours);

            if (!orderElement.getHoursGroups().isEmpty()) {
                orderElement.getHoursGroups().get(0).setCode(UUID.randomUUID().toString());
            }

        } else {

            orderElement = OrderLineGroup.createUnvalidated(UUID.randomUUID().toString());

            orderElement.useSchedulingDataFor(orderVersion);
        }

        List<OrderElement> children = new ArrayList<>();

        for (OrderElementDTO childrenTask : task.children) {
            children.add(convertImportTaskToOrderElement(orderVersion, childrenTask));
        }

        for (OrderElement child : children) {

            ((OrderLineGroup) orderElement).add(child);
        }

        orderElement.setName(task.name);

        orderElement.setDeadline(task.deadline);

        task.orderElement = orderElement;

        return orderElement;
    }

    /**
     * Creates a {@link TaskGroup} from a {@link OrderDTO}.
     *
     * @param project
     *            ImportData to extract data from
     *
     * @return TaskGroup TaskGroup with the data extracted.
     */
    @Override
    @Transactional
    public TaskGroup createTask(OrderDTO project, boolean importCalendar) {

        Order order = project.order;

        TaskSource taskSource = TaskSource.createForGroup(order.getCurrentSchedulingDataForVersion());

        TaskGroup taskGroup = taskSource.createTaskGroupWithoutDatesInitializedAndLinkItToTaskSource();

        BaseCalendar calendar = configurationDAO.getConfiguration().getDefaultCalendar();

        taskGroup.setCalendar(calendar);

        List<TaskElement> taskElements = new ArrayList<>();

        for (OrderElementDTO importTask : project.tasks) {

            taskElements.add(createTask(importTask, importCalendar));

        }

        for (MilestoneDTO milestone : project.milestones) {

            taskElements.add(createTaskMilestone(milestone));

        }

        for (TaskElement taskElement : taskElements) {
            taskGroup.addTaskElement(taskElement);
        }

        return taskGroup;

    }

    /**
     * Private method.
     *
     * It makes a {@link TaskMilestone} from a {@link MilestoneDTO}
     *
     * @param milestone
     *            MilestoneDTO to extract data from.
     *
     * @return TaskElement TaskElement that represent the data.
     */
    @Transactional
    private TaskElement createTaskMilestone(MilestoneDTO milestone) {

        TaskElement taskMilestone = TaskMilestone.create(milestone.startDate);

        taskMilestone.setName(milestone.name);

        setPositionConstraint((TaskMilestone) taskMilestone, milestone);

        milestone.taskElement = taskMilestone;

        return taskMilestone;
    }

    /**
     * It makes a {@link TaskElement} from a {@link OrderElementDTO}.
     *
     * @param task
     *            ImportTask to extract data from.
     *
     * @return TaskElement TaskElement that represent the data.
     */
    private TaskElement createTask(OrderElementDTO task, boolean importCalendar) {

        OrderElement orderElement = task.orderElement;

        TaskElement taskElement;

        TaskSource taskSource;

        if (task.children.size() == 0) {

            taskSource = TaskSource.create(orderElement.getCurrentSchedulingDataForVersion(),
                    orderElement.getHoursGroups());

            taskElement = taskSource.createTaskWithoutDatesInitializedAndLinkItToTaskSource();

            if (importCalendar && task.calendarName != null) {
                taskElement.setCalendar(findBaseCalendar(task.calendarName));
            }

            setPositionConstraint((Task) taskElement, task);

        } else {

            taskSource = TaskSource.createForGroup(orderElement.getCurrentSchedulingDataForVersion());

            taskElement = taskSource.createTaskGroupWithoutDatesInitializedAndLinkItToTaskSource();

            List<TaskElement> taskElements = new ArrayList<>();

            for (OrderElementDTO importTask : task.children) {

                taskElements.add(createTask(importTask, importCalendar));

            }

            if (task.milestones != null)
                for (MilestoneDTO milestone : task.milestones) {

                    taskElements.add(createTaskMilestone(milestone));

                }

            for (TaskElement childTaskElement : taskElements) {
                ((TaskGroup) taskElement).addTaskElement(childTaskElement);
            }

        }

        taskElement.setStartDate(task.startDate);
        taskElement.setEndDate(task.endDate);

        task.taskElement = taskElement;

        return taskElement;
    }

    /**
     * Sets the proper constraint to and a {@link Task}.
     *
     * @param importTask
     *            OrderElementDTO to extract data from.
     * @param task
     *            Task to set data on.
     */
    private void setPositionConstraint(Task task, OrderElementDTO importTask) {

        switch (importTask.constraint) {

        case AS_SOON_AS_POSSIBLE:
            task.getPositionConstraint().asSoonAsPossible();
            return;

        case AS_LATE_AS_POSSIBLE:
            task.getPositionConstraint().asLateAsPossible();
            return;

        case START_IN_FIXED_DATE:
            task.setIntraDayStartDate(IntraDayDate.startOfDay(LocalDate.fromDateFields(importTask.constraintDate)));
            Task.convertOnStartInFixedDate(task);
            return;

        case START_NOT_EARLIER_THAN:
            task.getPositionConstraint()
                    .notEarlierThan(IntraDayDate.startOfDay(LocalDate.fromDateFields(importTask.constraintDate)));
            return;

        case FINISH_NOT_LATER_THAN:
            task.getPositionConstraint().finishNotLaterThan(
                    IntraDayDate.startOfDay(LocalDate.fromDateFields(importTask.constraintDate)));
            return;

        default:
            return;
        }

    }

    /**
     * Sets the proper constraint to and a {@link TaskMiletone}
     *
     * @param milestone
     *            MilestoneDTO to extract data from.
     * @param taskMilestone
     *            TaskMilestone to set data on.
     */
    private void setPositionConstraint(TaskMilestone taskMilestone, MilestoneDTO milestone) {

        switch (milestone.constraint) {

        case AS_SOON_AS_POSSIBLE:
            taskMilestone.getPositionConstraint().asSoonAsPossible();
            return;

        case AS_LATE_AS_POSSIBLE:
            taskMilestone.getPositionConstraint().asLateAsPossible();
            return;

        case START_IN_FIXED_DATE:
            taskMilestone.getPositionConstraint()
                    .notEarlierThan(IntraDayDate.startOfDay(LocalDate.fromDateFields(milestone.constraintDate)));
            return;

        case START_NOT_EARLIER_THAN:
            taskMilestone.getPositionConstraint()
                    .notEarlierThan(IntraDayDate.startOfDay(LocalDate.fromDateFields(milestone.constraintDate)));
            return;

        case FINISH_NOT_LATER_THAN:
            taskMilestone.getPositionConstraint().finishNotLaterThan(
                    IntraDayDate.startOfDay(LocalDate.fromDateFields(milestone.constraintDate)));
            return;

        default:
            return;
        }

    }

    /**
     * Saves an {@link Order} which has all the data that we want to store in the database.
     * Also save all the related {@link TaskElement} and its {@link TaskSource}.
     *
     * @param order
     *            Order with the data.
     * @param taskGroup
     *            TaskGroup with the data. It also contains the link to the TaskSources.
     * @param dependencies
     */
    @Override
    @Transactional
    public void storeOrder(final Order order, final TaskGroup taskGroup, final List<Dependency> dependencies) {

        final List<TaskSource> taskSources = new ArrayList<>();

        taskSources.add(taskGroup.getTaskSource());

        for (TaskElement taskElement : taskGroup.getAllChildren()) {

            if (!taskElement.isMilestone()) {

                taskSources.add(taskElement.getTaskSource());

            }

        }

        orderDAO.save(order);

        taskDAO.save(taskGroup);

        for (TaskSource taskSource : taskSources) {

            taskSource.validate();
            taskSourceDAO.save(taskSource);

        }

        for (Dependency dependency : dependencies) {

            dependencyDAO.save(dependency);

        }

    }

    /**
     * Creates a list of {@link Dependency} from a {@link OrderDTO}.
     *
     * @param importData
     *            ImportData to extract data from
     *
     * @return {@link List<Dependency>} with the data extracted.
     */
    @Override
    public List<Dependency> createDependencies(OrderDTO importData) {

        List<Dependency> dependencies = new ArrayList<>();

        for (DependencyDTO dependencyDTO : importData.dependencies) {

            TaskElement origin = null;
            TaskElement destination = null;

            if (dependencyDTO.origin != null && dependencyDTO.origin.getTaskAssociated() != null)
                origin = dependencyDTO.origin.getTaskAssociated();

            if (dependencyDTO.destination != null && dependencyDTO.destination.getTaskAssociated() != null)
                destination = dependencyDTO.destination.getTaskAssociated();

            if (origin != null && destination != null) {
                dependencies.add(Dependency.create(origin, destination, toLPType(dependencyDTO.type)));
            }
        }

        return dependencies;
    }

    /**
     * Return the equivalent {@link Type} of a {@link TypeOfDependencyDTO}.
     *
     * @param type
     *            TypeOfDependencyDTO to extract data from.
     * @return Type equivalent type.
     */
    private Type toLPType(TypeOfDependencyDTO type) {

        switch (type) {

        case END_START:
            return Type.END_START;

        case START_START:
            return Type.START_START;

        case END_END:
            return Type.END_END;

        case START_END:
            return Type.START_END;

        default:
            return null;

        }
    }

    /**
     * Return the {@link BaseCalendar} with the same name as the string given.
     *
     * @param name
     *            String with the name that we want to find.
     * @return BaseCalendar Calendar.
     */
    private BaseCalendar findBaseCalendar(String name) {

        List<BaseCalendar> baseCalendars = baseCalendarDAO.findByName(name);

        BaseCalendar calendar;

        for (BaseCalendar baseCalendar : baseCalendars) {
            if (baseCalendar.getName().equals(name)) {

                calendar = baseCalendar;
                return calendar;

            }
        }

        throw new ValidationException(_("Linked calendar not found"));
    }
}