org.libreplan.business.templates.entities.OrderElementTemplate.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.business.templates.entities.OrderElementTemplate.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.libreplan.business.templates.entities;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.NonUniqueResultException;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Min;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.libreplan.business.advance.entities.AdvanceAssignmentTemplate;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.materials.entities.MaterialAssignment;
import org.libreplan.business.materials.entities.MaterialAssignmentTemplate;
import org.libreplan.business.orders.entities.HoursGroup;
import org.libreplan.business.orders.entities.ICriterionRequirable;
import org.libreplan.business.orders.entities.InfoComponent;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLineGroup;
import org.libreplan.business.orders.entities.SchedulingState;
import org.libreplan.business.orders.entities.SchedulingState.Type;
import org.libreplan.business.qualityforms.entities.QualityForm;
import org.libreplan.business.requirements.entities.CriterionRequirement;
import org.libreplan.business.requirements.entities.DirectCriterionRequirement;
import org.libreplan.business.requirements.entities.IndirectCriterionRequirement;
import org.libreplan.business.templates.daos.IOrderElementTemplateDAO;
import org.libreplan.business.trees.ITreeNode;
import org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException;

/**
 * @author scar Gonzlez Fernndez <ogonzalez@igalia.com>
 */
public abstract class OrderElementTemplate extends BaseEntity
        implements ICriterionRequirable, ITreeNode<OrderElementTemplate> {

    private static final Log LOG = LogFactory.getLog(OrderElementTemplate.class);

    private SchedulingState.Type schedulingStateType;

    private InfoComponent infoComponent;

    private Integer startAsDaysFromBeginning;

    private Integer deadlineAsDaysFromBeginning;

    private OrderLineGroupTemplate parent;

    private Set<CriterionRequirement> criterionRequirements = new HashSet<>();

    private Set<MaterialAssignmentTemplate> materialAssignments = new HashSet<>();

    private Set<Label> labels = new HashSet<>();

    private Set<QualityForm> qualityForms = new HashSet<>();

    private Set<AdvanceAssignmentTemplate> advanceAssignmentTemplates = new HashSet<>();

    private SchedulingState schedulingState;

    private OrderElement origin;

    public static <T extends OrderElementTemplate> T create(T beingBuilt, OrderElement origin) {
        InfoComponent infoComponentCopied = origin.getInfoComponent().copy();
        Order order = origin.getOrder();
        Days fromBeginningToStart = daysBetween(order.getInitDate(), origin.getInitDate());
        Days fromBeginningToEnd = daysBetween(order.getInitDate(), origin.getDeadline());
        beingBuilt.setMaterialAssignments(copyMaterialAssignmentsFrom(beingBuilt, origin.getMaterialAssignments()));

        beingBuilt.setCriterionRequirements(
                copyDirectCriterionRequirements(beingBuilt, origin.getDirectCriterionRequirement()));

        beingBuilt.addLabels(origin.getLabels());
        beingBuilt.setQualityForms(origin.getQualityForms());

        beingBuilt.setAdvanceAssignmentTemplates(
                copyDirectAdvanceAssignments(beingBuilt, origin.getDirectAdvanceAssignments()));

        beingBuilt.setInfoComponent(infoComponentCopied);
        beingBuilt.setSchedulingStateType(origin.getSchedulingStateType());
        assignDates(beingBuilt, fromBeginningToStart, fromBeginningToEnd);
        beingBuilt.setOrigin(origin);

        return create(beingBuilt);
    }

    /**
     * Copy only {@link DirectCriterionRequirement}.
     *
     * @param beingBuilt
     * @param criterionRequirements
     * @return {@link Set<CriterionRequirement>}
     */
    private static Set<CriterionRequirement> copyDirectCriterionRequirements(OrderElementTemplate beingBuilt,
            Collection<DirectCriterionRequirement> criterionRequirements) {

        Set<CriterionRequirement> result = new HashSet<>();

        for (DirectCriterionRequirement each : criterionRequirements) {

            DirectCriterionRequirement newDirectCriterionRequirement = DirectCriterionRequirement.copyFrom(each,
                    beingBuilt);

            result.add(newDirectCriterionRequirement);
        }

        return result;
    }

    private static Set<AdvanceAssignmentTemplate> copyDirectAdvanceAssignments(OrderElementTemplate beingBuilt,
            Set<DirectAdvanceAssignment> directAdvanceAssignments) {

        Set<AdvanceAssignmentTemplate> result = new HashSet<>();
        for (DirectAdvanceAssignment each : directAdvanceAssignments) {
            result.add(AdvanceAssignmentTemplate.convert(beingBuilt, each));
        }

        return result;
    }

    public static <T extends OrderElementTemplate> T createNew(T beingBuilt) {
        beingBuilt.setInfoComponent(new InfoComponent());
        assignDates(beingBuilt, null, null);

        return create(beingBuilt);
    }

    private static Set<MaterialAssignmentTemplate> copyMaterialAssignmentsFrom(OrderElementTemplate beingBuilt,
            Collection<? extends MaterialAssignment> assignments) {

        Set<MaterialAssignmentTemplate> result = new HashSet<>();
        for (MaterialAssignment each : assignments) {
            result.add(MaterialAssignmentTemplate.copyFrom(each, beingBuilt));
        }

        return result;
    }

    private static void assignDates(OrderElementTemplate beingBuilt, Days fromBeginningToStart,
            Days fromBeginningToEnd) {

        Validate.isTrue(isNullOrPositive(fromBeginningToStart));
        Validate.isTrue(isNullOrPositive(fromBeginningToEnd));
        beingBuilt.startAsDaysFromBeginning = daysToInteger(fromBeginningToStart);
        beingBuilt.deadlineAsDaysFromBeginning = daysToInteger(fromBeginningToEnd);
    }

    private static Days daysBetween(Date start, Date end) {
        if (start == null || end == null) {
            return null;
        }

        return Days.daysBetween(asDateTime(start), asDateTime(end));
    }

    private static DateTime asDateTime(Date date) {
        return new DateTime(date);
    }

    private static boolean isNullOrPositive(Days days) {
        return days == null || days.getDays() >= 0;
    }

    private static Integer daysToInteger(Days days) {
        return days != null ? days.getDays() : null;
    }

    protected <T extends OrderElement> T setupElementParts(T orderElement) {
        setupInfoComponent(orderElement);
        setupDates(orderElement);
        setupCriterionRequirements(orderElement);
        setupMaterialAssignments(orderElement);
        setupLabels(orderElement);
        setupQualityForms(orderElement);
        setupAdvances(orderElement);

        return orderElement;
    }

    protected <T extends OrderElement> T setupSchedulingStateType(T orderElement) {
        orderElement.initializeType(schedulingStateType);

        return orderElement;
    }

    protected <T extends OrderElement> T setupVersioningInfo(OrderLineGroup parent, T orderElement) {
        orderElement.useSchedulingDataFor(parent.getCurrentOrderVersion());

        return orderElement;
    }

    private void setupInfoComponent(OrderElement orderElement) {
        orderElement.setName(getName());
        orderElement.setDescription(getDescription());
    }

    private void setupDates(OrderElement orderElement) {
        Date orderInitDate = orderElement.getOrder().getInitDate();

        if (getStartAsDaysFromBeginning() != null) {
            orderElement.setInitDate(plusDays(orderInitDate, getStartAsDaysFromBeginning()));
        }

        if (getDeadlineAsDaysFromBeginning() != null) {
            orderElement.setDeadline(plusDays(orderInitDate, getDeadlineAsDaysFromBeginning()));
        }
    }

    private Date plusDays(Date date, Integer days) {
        LocalDate localDate = new LocalDate(date);

        return localDate.plusDays(days).toDateTimeAtStartOfDay().toDate();
    }

    private void setupCriterionRequirements(OrderElement orderElement) {
        for (DirectCriterionRequirement each : getDirectCriterionRequirements()) {
            if (orderElement.canAddCriterionRequirement(each)) {
                orderElement.addCriterionRequirement(DirectCriterionRequirement.copyFrom(each, orderElement));
            }
        }
    }

    private void setupMaterialAssignments(OrderElement orderElement) {
        for (MaterialAssignmentTemplate each : materialAssignments) {
            orderElement.addMaterialAssignment(each.createAssignment(orderElement));
        }
    }

    private void setupLabels(OrderElement orderElement) {
        for (Label each : getLabels()) {
            if (orderElement.checkAncestorsNoOtherLabelRepeated(each)) {
                orderElement.addLabel(each);
            }
        }
    }

    private void setupQualityForms(OrderElement orderElement) {
        for (QualityForm each : qualityForms) {
            orderElement.addTaskQualityForm(each);
        }
    }

    private void setupAdvances(OrderElement orderElement) {
        for (AdvanceAssignmentTemplate each : advanceAssignmentTemplates) {
            try {
                orderElement.addAdvanceAssignment(each.createAdvanceAssignment(orderElement));
            } catch (Exception e) {
                String errorMessage = "error adding advance assignment to newly instantiated orderElement. Ignoring it";
                LOG.warn(errorMessage, e);
            }
        }
    }

    public abstract OrderElement createElement(OrderLineGroup parent);

    public SchedulingState getSchedulingState() {
        if (schedulingState == null) {

            schedulingState = SchedulingState.createSchedulingState(getSchedulingStateType(), getChildrenStates(),
                    newType -> schedulingStateType = newType);
        }

        return schedulingState;
    }

    private List<SchedulingState> getChildrenStates() {
        List<SchedulingState> result = new ArrayList<>();
        for (OrderElementTemplate each : getChildren()) {
            result.add(each.getSchedulingState());
        }

        return result;
    }

    public SchedulingState.Type getSchedulingStateType() {
        if (schedulingStateType == null) {
            schedulingStateType = Type.NO_SCHEDULED;
        }

        return schedulingStateType;
    }

    protected void setSchedulingStateType(SchedulingState.Type schedulingStateType) {
        this.schedulingStateType = schedulingStateType;
    }

    public OrderLineGroupTemplate getParent() {
        return parent;
    }

    protected void setParent(OrderLineGroupTemplate parent) {
        this.parent = parent;
    }

    protected InfoComponent getInfoComponent() {
        if (infoComponent == null) {
            infoComponent = new InfoComponent();
        }

        return infoComponent;
    }

    protected void setInfoComponent(InfoComponent infoComponent) {
        this.infoComponent = infoComponent;
    }

    /**
     * @return a description of the type or template this object is.
     */
    public abstract String getType();

    public abstract List<OrderElementTemplate> getChildrenTemplates();

    @Min(0)
    public Integer getDeadlineAsDaysFromBeginning() {
        return deadlineAsDaysFromBeginning;
    }

    @Min(0)
    public Integer getStartAsDaysFromBeginning() {
        return startAsDaysFromBeginning;
    }

    public void setStartAsDaysFromBeginning(Integer days) {
        this.startAsDaysFromBeginning = days;
    }

    public void setDeadlineAsDaysFromBeginning(Integer days) {
        this.deadlineAsDaysFromBeginning = days;
    }

    public String getDescription() {
        return getInfoComponent().getDescription();
    }

    public void setDescription(String description) {
        getInfoComponent().setDescription(description);
    }

    @NotEmpty(message = "name not specified")
    public String getName() {
        return getInfoComponent().getName();
    }

    public void setName(String name) {
        getInfoComponent().setName(name);
    }

    @Override
    public OrderElementTemplate getThis() {
        return this;
    }

    protected void copyTo(OrderElementTemplate result) {
        result.setName(getName());
        result.setDescription(getDescription());
        result.setDeadlineAsDaysFromBeginning(getDeadlineAsDaysFromBeginning());
        result.setStartAsDaysFromBeginning(getStartAsDaysFromBeginning());
    }

    @Valid
    public Set<MaterialAssignmentTemplate> getMaterialAssignments() {
        return Collections.unmodifiableSet(materialAssignments);
    }

    protected void setMaterialAssignments(Set<MaterialAssignmentTemplate> newMaterialAssignments) {
        this.materialAssignments = newMaterialAssignments;
    }

    public void addMaterialAssignment(MaterialAssignmentTemplate materialAssignment) {
        Validate.notNull(materialAssignment);
        materialAssignments.add(materialAssignment);
    }

    public void removeMaterialAssignment(MaterialAssignmentTemplate materialAssignment) {
        materialAssignments.remove(materialAssignment);
    }

    public BigDecimal getTotalMaterialAssignmentPrice() {
        BigDecimal result = BigDecimal.ZERO;
        for (MaterialAssignmentTemplate each : materialAssignments) {
            result = result.add(each.getTotalPrice());
        }

        return result;
    }

    public BigDecimal getTotalMaterialAssignmentUnits() {
        BigDecimal result = BigDecimal.ZERO;
        for (MaterialAssignmentTemplate each : materialAssignments) {
            if (each.getUnits() != null) {
                result = result.add(each.getUnits());
            }
        }

        return result;
    }

    public abstract boolean isLeaf();

    @Valid
    public Set<Label> getLabels() {
        return Collections.unmodifiableSet(labels);
    }

    public void addLabel(Label label) {
        Validate.notNull(label);
        this.labels.add(label);
    }

    public void addLabels(Collection<Label> labels) {
        Validate.notNull(labels);
        this.labels.addAll(labels);
    }

    public void removeLabel(Label label) {
        this.labels.remove(label);
    }

    public Set<QualityForm> getQualityForms() {
        return Collections.unmodifiableSet(qualityForms);
    }

    protected void setQualityForms(Set<QualityForm> qualityForms) {
        this.qualityForms = qualityForms;
    }

    public void addQualityForm(QualityForm qualityForm) {
        qualityForms.add(qualityForm);
    }

    public void removeQualityForm(QualityForm qualityForm) {
        qualityForms.remove(qualityForm);
    }

    @Valid
    public Set<AdvanceAssignmentTemplate> getAdvanceAssignmentTemplates() {
        return Collections.unmodifiableSet(advanceAssignmentTemplates);
    }

    protected void setAdvanceAssignmentTemplates(Set<AdvanceAssignmentTemplate> advanceAssignmentTemplates) {
        this.advanceAssignmentTemplates = advanceAssignmentTemplates;
    }

    public boolean isRoot() {
        return getParent() == null;
    }

    @AssertTrue(message = "template name is already in use")
    public boolean isUniqueRootTemplateNameConstraint() {
        if (getParent() != null) {
            return true;
        }

        IOrderElementTemplateDAO orderElementTemplateDAO = Registry.getOrderElementTemplateDAO();
        if (isNewObject()) {
            return !orderElementTemplateDAO.existsRootByNameAnotherTransaction(this);
        } else {
            try {
                OrderElementTemplate template = orderElementTemplateDAO.findUniqueRootByName(getName());

                return template.getId().equals(getId());
            } catch (InstanceNotFoundException e) {
                return true;
            } catch (NonUniqueResultException e) {
                return false;
            } catch (HibernateOptimisticLockingFailureException e) {
                return true;
            }
        }
    }

    public List<OrderElementTemplate> getAllChildren() {
        List<OrderElementTemplate> children = getChildrenTemplates();
        List<OrderElementTemplate> result = new ArrayList<>();

        for (OrderElementTemplate orderElement : children) {
            result.add(orderElement);
            result.addAll(orderElement.getAllChildren());
        }

        return result;
    }

    @Valid
    @Override
    public Set<CriterionRequirement> getCriterionRequirements() {
        return Collections.unmodifiableSet(criterionRequirements);
    }

    protected void setCriterionRequirements(Set<CriterionRequirement> criterionRequirements) {
        this.criterionRequirements = criterionRequirements;
    }

    public abstract List<HoursGroup> getHoursGroups();

    public abstract Integer getWorkHours();

    /**
     * Operations for manipulating CriterionRequirement.
     */

    protected CriterionRequirementTemplateHandler criterionRequirementHandler = CriterionRequirementTemplateHandler
            .getInstance();

    public void setValidCriterionRequirement(IndirectCriterionRequirement requirement, boolean valid) {
        requirement.setValid(valid);
        criterionRequirementHandler.propagateValidCriterionRequirement(this, requirement.getParent(), valid);
    }

    public void removeDirectCriterionRequirement(DirectCriterionRequirement criterionRequirement) {
        criterionRequirementHandler.propagateRemoveCriterionRequirement(this, criterionRequirement);
        removeCriterionRequirement(criterionRequirement);

    }

    @Override
    public void removeCriterionRequirement(CriterionRequirement requirement) {
        criterionRequirements.remove(requirement);
        if (requirement instanceof IndirectCriterionRequirement) {
            ((IndirectCriterionRequirement) requirement).getParent().getChildren().remove(requirement);
        }
    }

    @Override
    public void addCriterionRequirement(CriterionRequirement criterionRequirement) {
        criterionRequirementHandler.addCriterionRequirement(this, criterionRequirement);
    }

    public void addDirectCriterionRequirement(CriterionRequirement criterionRequirement) {
        criterionRequirementHandler.addDirectCriterionRequirement(this, criterionRequirement);
    }

    public void addIndirectCriterionRequirement(IndirectCriterionRequirement criterionRequirement) {
        criterionRequirementHandler.addIndirectCriterionRequirement(this, criterionRequirement);
    }

    protected void basicAddCriterionRequirement(CriterionRequirement criterionRequirement) {
        criterionRequirement.setOrderElementTemplate(this);
        this.criterionRequirements.add(criterionRequirement);
    }

    public void updateCriterionRequirements() {
        criterionRequirementHandler.updateMyCriterionRequirements(this);
        criterionRequirementHandler.propagateUpdateCriterionRequirements(this);
    }

    public boolean canAddCriterionRequirement(DirectCriterionRequirement newRequirement) {
        return criterionRequirementHandler.canAddCriterionRequirement(this, newRequirement);
    }

    protected Set<IndirectCriterionRequirement> getIndirectCriterionRequirement() {
        return criterionRequirementHandler.getIndirectCriterionRequirement(criterionRequirements);
    }

    public Set<DirectCriterionRequirement> getDirectCriterionRequirements() {
        return criterionRequirementHandler.getDirectCriterionRequirement(criterionRequirements);
    }

    public Order getOrder() {
        return (parent != null) ? parent.getOrder() : null;
    }

    public OrderElement getOrigin() {
        return origin;
    }

    public void setOrigin(OrderElement origin) {
        this.origin = origin;
    }

    public abstract BigDecimal getBudget();

    public abstract boolean isOrderTemplate();

    @Override
    public boolean isUpdatedFromTimesheets() {
        return false;
    }

    @Override
    public boolean isJiraIssue() {
        return false;
    }

}