org.libreplan.web.orders.OrderElementTreeController.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.web.orders.OrderElementTreeController.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.web.orders;

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

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.libreplan.business.common.daos.IConnectorDAO;
import org.libreplan.business.common.entities.Connector;
import org.libreplan.business.common.entities.EntitySequence;
import org.libreplan.business.common.entities.PredefinedConnectorProperties;
import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.labels.entities.Label;
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.SchedulingState;
import org.libreplan.business.requirements.entities.CriterionRequirement;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.web.common.FilterUtils;
import org.libreplan.web.common.IMessagesForUser;
import org.libreplan.web.common.Level;
import org.libreplan.web.common.Util;
import org.libreplan.web.common.components.bandboxsearch.BandboxMultipleSearch;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.libreplan.web.common.components.finders.FilterPair;
import org.libreplan.web.common.components.finders.OrderElementFilterEnum;
import org.libreplan.web.common.components.finders.TaskElementFilterEnum;
import org.libreplan.web.orders.assigntemplates.TemplateFinderPopup;
import org.libreplan.web.security.SecurityUtils;
import org.libreplan.web.templates.IOrderTemplatesControllerEntryPoints;
import org.libreplan.web.tree.TreeController;
import org.zkoss.ganttz.IPredicate;
import org.zkoss.ganttz.util.ComponentsFinder;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.A;
import org.zkoss.zul.Button;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Popup;
import org.zkoss.zul.Tab;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.TreeModel;
import org.zkoss.zul.Treechildren;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.Vbox;
import org.zkoss.zul.Treerow;
import org.zkoss.zul.impl.InputElement;

/**
 * Controller for {@link OrderElement} tree view of {@link Order} entities.
 * <br />
 * @author Lorenzo Tilve ?lvaro <ltilve@igalia.com>
 * @author Manuel Rego Casasnovas <mrego@igalia.com>
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 * @author Diego Pino Garca <dpino@igalia.com>
 * @author Vova Perebykivskyi <vova@libreplan-enterprise.com>
 */
public class OrderElementTreeController extends TreeController<OrderElement> {

    private Vbox orderElementFilter;

    private BandboxMultipleSearch bdFiltersOrderElement;

    private Datebox filterStartDateOrderElement;

    private Datebox filterFinishDateOrderElement;

    private Checkbox labelsWithoutInheritance;

    private Textbox filterNameOrderElement;

    private OrderElementTreeitemRenderer renderer = new OrderElementTreeitemRenderer();

    private final IOrderModel orderModel;

    private final OrderElementController orderElementController;

    private transient IPredicate predicate;

    private OrderElementOperations operationsForOrderElement;

    private final IMessagesForUser messagesForUser;

    private IConnectorDAO connectorDAO;

    private Tab tabGeneralData;

    private TemplateFinderPopup templateFinderPopup;

    public OrderElementTreeController(IOrderModel orderModel, OrderElementController orderElementController,
            IMessagesForUser messagesForUser) {

        super(OrderElement.class);
        this.orderModel = orderModel;
        this.orderElementController = orderElementController;
        this.messagesForUser = messagesForUser;
        initializeOperationsForOrderElement();
    }

    public List<Label> getLabels() {
        return orderModel.getLabels();
    }

    @Override
    public OrderElementTreeitemRenderer getRenderer() {
        return renderer;
    }

    /**
     * Initializes operationsForOrderTemplate.
     * A reference to variables tree and orderTemplates will be set later in doAfterCompose()
     */
    private void initializeOperationsForOrderElement() {
        operationsForOrderElement = OrderElementOperations.build().treeController(this).orderModel(this.orderModel)
                .orderElementController(this.orderElementController);
    }

    public OrderElementController getOrderElementController() {
        return orderElementController;
    }

    @Override
    protected OrderElementTreeModel getModel() {
        return orderModel.getOrderElementTreeModel();
    }

    /**
     * Operations for each node.
     */

    public void editSelectedElement() {
        operationsForOrderElement.editSelectedElement();
    }

    public void createTemplateFromSelectedElement() {
        operationsForOrderElement.createTemplateFromSelectedElement();
    }

    public void moveSelectedElementUp() {
        operationsForOrderElement.moveSelectedElementUp();
    }

    public void moveSelectedElementDown() {
        operationsForOrderElement.moveSelectedElementDown();
    }

    public void indentSelectedElement() {
        operationsForOrderElement.indentSelectedElement();
    }

    public void unindentSelectedElement() {
        operationsForOrderElement.unindentSelectedElement();
    }

    public void deleteSelectedElement() {
        operationsForOrderElement.deleteSelectedElement();
    }

    public void createFromTemplate() {
        templateFinderPopup.openForSubElemenetCreation(tree, "after_pointer", template -> {
            OrderLineGroup parent = (OrderLineGroup) getModel().getRoot();
            orderModel.createFrom(parent, template);
            getModel().addNewlyAddedChildrenOf(parent);

            reloadTreeUIAfterChanges();
        });
    }

    @Override
    protected void reloadTreeUIAfterChanges() {
        tree.setModel(getFilteredTreeModel());
        tree.onInitRender();
        tree.invalidate();
    }

    void doEditFor() {
        Util.reloadBindings(tree);
    }

    public void disabledCodeBoxes(boolean disabled) {
        Set<Treeitem> childrenSet = new HashSet<>();
        Treechildren treeChildren = tree.getTreechildren();

        if (treeChildren != null) {
            childrenSet.addAll(treeChildren.getItems());
        }

        for (Treeitem each : childrenSet) {
            disableCodeBoxes(each, disabled);
        }
    }

    private void disableCodeBoxes(Treeitem item, boolean disabled) {
        Treerow row = item.getTreerow();
        InputElement codeBox = (InputElement) (row.getChildren().get(1)).getChildren().get(0);
        codeBox.setDisabled(disabled);
        codeBox.invalidate();

        Set<Treeitem> childrenSet = new HashSet<>();
        Treechildren children = item.getTreechildren();

        if (children != null) {
            childrenSet.addAll(children.getItems());
        }

        for (Treeitem each : childrenSet) {
            disableCodeBoxes(each, disabled);
        }
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        orderElementFilter.getChildren().clear();
        appendExpandCollapseButton();

        connectorDAO = (IConnectorDAO) SpringUtil.getBean("connectorDAO");

        // Configuration of the order elements filter
        Component filterComponent = Executions.createComponents("/orders/_orderElementTreeFilter.zul",
                orderElementFilter, new HashMap<String, String>());

        IOrderTemplatesControllerEntryPoints orderTemplates = (IOrderTemplatesControllerEntryPoints) SpringUtil
                .getBean("orderTemplates");

        filterComponent.setAttribute("treeController", this, true);
        bdFiltersOrderElement = (BandboxMultipleSearch) filterComponent.getFellow("bdFiltersOrderElement");
        Popup filterOptionsPopup = (Popup) filterComponent.getFellow("filterOptionsPopup");
        filterStartDateOrderElement = (Datebox) filterOptionsPopup.getFellow("filterStartDateOrderElement");
        filterFinishDateOrderElement = (Datebox) filterOptionsPopup.getFellow("filterFinishDateOrderElement");
        labelsWithoutInheritance = (Checkbox) filterOptionsPopup.getFellow("labelsWithoutInheritance");
        filterNameOrderElement = (Textbox) filterComponent.getFellow("filterNameOrderElement");
        labelsWithoutInheritance = (Checkbox) filterComponent.getFellow("labelsWithoutInheritance");
        templateFinderPopup = (TemplateFinderPopup) comp.getFellow("templateFinderPopupAtTree");
        operationsForOrderElement.tree(tree).orderTemplates(orderTemplates);

        importOrderFiltersFromSession();
        disableCreateTemplateButtonIfNeeded(comp);
    }

    private void importOrderFiltersFromSession() {
        Order order = orderModel.getOrder();
        filterNameOrderElement.setValue(FilterUtils.readOrderTaskName(order));
        filterStartDateOrderElement.setValue(FilterUtils.readOrderStartDate(order));
        filterFinishDateOrderElement.setValue(FilterUtils.readOrderEndDate(order));

        if (FilterUtils.readOrderParameters(order) != null) {
            for (FilterPair each : FilterUtils.readOrderParameters(order)) {
                if (toOrderFilterEnum(each) != null) {
                    bdFiltersOrderElement.addSelectedElement(toOrderFilterEnum(each));
                }
            }
        }
        if (FilterUtils.readOrderInheritance(order) != null) {
            labelsWithoutInheritance.setChecked(FilterUtils.readOrderInheritance(order));
        }
    }

    private FilterPair toOrderFilterEnum(FilterPair each) {
        switch ((TaskElementFilterEnum) each.getType()) {

        case Label:
            return new FilterPair(OrderElementFilterEnum.Label, each.getPattern(), each.getValue());

        case Criterion:
            return new FilterPair(OrderElementFilterEnum.Criterion, each.getPattern(), each.getValue());

        case Resource:
            // Resources are discarded on WBS filter
        default:
            return null;
        }
    }

    private void disableCreateTemplateButtonIfNeeded(Component comp) {
        Button createTemplateButton = (Button) comp.getFellowIfAny("createTemplateButton");
        if (createTemplateButton != null) {
            if (!SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_TEMPLATES)) {
                createTemplateButton.setDisabled(true);
                createTemplateButton.setTooltiptext(_("Not enough permissions to create templates"));
            }
        }
    }

    private void appendExpandCollapseButton() {
        List<Component> children = orderElementFilter.getParent().getChildren();

        Button button = (Button) ComponentsFinder.findById("expandAllButton", children);

        if (button != null) {
            if (button.getSclass().equals("planner-command clicked")) {
                button.setSclass("planner-command");
                button.invalidate();
            }
            return;
        }

        // Append expand/collapse button
        final Button expandAllButton = new Button();
        expandAllButton.setId("expandAllButton");
        expandAllButton.setClass("planner-command");
        expandAllButton.setTooltiptext(_("Expand/Collapse all"));
        expandAllButton.setImage("/common/img/ico_expand.png");

        expandAllButton.addEventListener("onClick", event -> {
            if (expandAllButton.getSclass().equals("planner-command")) {
                expandAll();
                expandAllButton.setSclass("planner-command clicked");
            } else {
                collapseAll();
                expandAllButton.setSclass("planner-command");
            }
        });

        children.add(expandAllButton);
    }

    public void expandAll() {
        Set<Treeitem> childrenSet = new HashSet<>();
        Treechildren children = tree.getTreechildren();

        if (children != null) {
            childrenSet.addAll(children.getItems());
        }
        childrenSet.forEach(this::expandAll);
    }

    private void expandAll(Treeitem item) {
        item.setOpen(true);

        Set<Treeitem> childrenSet = new HashSet<>();
        Treechildren children = item.getTreechildren();

        if (children != null) {
            childrenSet.addAll(children.getItems());
        }

        childrenSet.forEach(this::expandAll);
    }

    public void collapseAll() {
        Treechildren children = tree.getTreechildren();
        for (Treeitem each : (children.getItems())) {
            each.setOpen(false);
        }
    }

    public Map<OrderElement, Textbox> getOrderElementCodeTextboxes() {
        return getRenderer().getCodeTextboxByElement();
    }

    public class OrderElementTreeitemRenderer extends Renderer {

        public OrderElementTreeitemRenderer() {
        }

        @Override
        protected void addDescriptionCell(OrderElement element) {
            addTaskNameCell(element);
        }

        private void addTaskNameCell(final OrderElement orderElementForThisRow) {
            int[] path = getModel().getPath(orderElementForThisRow);
            String cssClass = "depth_" + path.length;

            Textbox textBox = Util.bind(new Textbox(), () -> orderElementForThisRow.getName(),
                    value -> orderElementForThisRow.setName(value));

            if (readOnly) {
                textBox.setDisabled(true);
            }

            textBox.setConstraint("no empty:" + _("cannot be empty"));
            addCell(cssClass, textBox);
            putNameTextbox(orderElementForThisRow, textBox);
        }

        @Override
        protected SchedulingState getSchedulingStateFrom(OrderElement currentElement) {
            return currentElement.getSchedulingState();
        }

        @Override
        protected void onDoubleClickForSchedulingStateCell(final OrderElement currentOrderElement) {
            IOrderElementModel model = orderModel.getOrderElementModel(currentOrderElement);
            orderElementController.openWindow(model);
            updateColumnsFor(currentOrderElement);
        }

        @Override
        protected void addCodeCell(final OrderElement orderElement) {
            if (orderElement.isJiraIssue()) {
                addHyperlink(orderElement);
            } else {
                addTextbox(orderElement);
            }
        }

        private void addTextbox(final OrderElement orderElement) {
            Textbox textBoxCode = new Textbox();

            Util.bind(textBoxCode, () -> orderElement.getCode(), value -> orderElement.setCode(value));

            textBoxCode.setConstraint((comp, value) -> {
                if (!orderElement.isFormatCodeValid((String) value)) {

                    throw new WrongValueException(comp,
                            _("Value is not valid.\n Code cannot contain chars like '_' \n "
                                    + "and should not be empty"));
                }
            });

            if (orderModel.isCodeAutogenerated() || readOnly) {
                textBoxCode.setDisabled(true);
            }

            addCell(textBoxCode);
            putCodeTextbox(orderElement, textBoxCode);
        }

        private void addHyperlink(final OrderElement orderElement) {
            String code = orderElement.getCode();
            A hyperlink = new A(code);

            Connector connector = connectorDAO.findUniqueByName(PredefinedConnectors.JIRA.getName());
            if (connector == null) {
                return;
            }

            String jiraUrl = connector.getPropertiesAsMap().get(PredefinedConnectorProperties.SERVER_URL);

            String codeWithoutPrefix = StringUtils.removeStart(code,
                    PredefinedConnectorProperties.JIRA_CODE_PREFIX);

            codeWithoutPrefix = StringUtils.removeStart(codeWithoutPrefix,
                    orderElement.getOrder().getCode() + EntitySequence.CODE_SEPARATOR_CHILDREN);

            hyperlink.setHref(jiraUrl + "/browse/" + codeWithoutPrefix);

            if (orderModel.isCodeAutogenerated() || readOnly) {
                hyperlink.setDisabled(true);
            }

            addCell(hyperlink);
        }

        void addInitDateCell(final OrderElement currentOrderElement) {
            DynamicDatebox dynamicDatebox = new DynamicDatebox(() -> currentOrderElement.getInitDate(),
                    value -> currentOrderElement.setInitDate(value));

            if (readOnly) {
                dynamicDatebox.setDisabled(true);
            }
            addDateCell("init-date-cell", dynamicDatebox);
            putInitDateDynamicDatebox(currentOrderElement, dynamicDatebox);
            reduceWidthOfDateBoxes(dynamicDatebox);
        }

        void addEndDateCell(final OrderElement currentOrderElement) {
            DynamicDatebox dynamicDatebox = new DynamicDatebox(() -> currentOrderElement.getDeadline(),
                    value -> currentOrderElement.setDeadline(value));

            if (readOnly || (currentOrderElement.getTaskSource() != null
                    && currentOrderElement.getTaskSource().getTask().isSubcontracted())) {

                dynamicDatebox.setDisabled(true);
            }

            addDateCell("end-date-cell", dynamicDatebox);
            putEndDateDynamicDatebox(currentOrderElement, dynamicDatebox);
            reduceWidthOfDateBoxes(dynamicDatebox);
        }

        /**
         * Decrease width of components in ZK8.
         */
        private void reduceWidthOfDateBoxes(DynamicDatebox dynamicDatebox) {
            Textbox textbox = dynamicDatebox.getDateTextBox();
            String[] strings = textbox.getWidth().split("px");
            textbox.setWidth(Integer.toString(Integer.valueOf(strings[0]) - 10));
        }

        @Override
        protected void addOperationsCell(final Treeitem item, final OrderElement currentOrderElement) {
            addCell(createEditButton(item), createRemoveButton(currentOrderElement));
        }

        private Button createEditButton(final Treeitem item) {

            return createButton("/common/img/ico_editar1.png", _("Edit"), "/common/img/ico_editar.png", "icono",
                    event -> showEditionOrderElement(item));
        }

        @Override
        public void removeCodeTextbox(OrderElement key) {
            super.removeCodeTextbox(key);
        }

        public void addResourcesBudgetCell(final OrderElement currentElement) {
            BigDecimal value = currentElement.getSubstractedBudget();
            Textbox autoBudgetCell = new Textbox(Util.addCurrencySymbol(value));
            autoBudgetCell.setDisabled(true);
            addCell(autoBudgetCell);
        }

    }

    @Override
    protected boolean isPredicateApplied() {
        return (predicate != null) && !((OrderElementPredicate) predicate).isEmpty();
    }

    /**
     * Apply filter to order elements in current order.
     */
    public void onApplyFilter() {
        writeFilterParameters();
        OrderElementPredicate predicate = createPredicate();
        this.predicate = predicate;

        if (predicate != null) {
            filterByPredicate(predicate);
        } else {
            showAllOrderElements();
        }
    }

    private void writeFilterParameters() {
        Order order = orderModel.getOrder();
        FilterUtils.writeOrderStartDate(order, filterStartDateOrderElement.getValue());
        FilterUtils.writeOrderEndDate(order, filterFinishDateOrderElement.getValue());
        FilterUtils.writeOrderTaskName(order, filterNameOrderElement.getValue());
        FilterUtils.writeOrderInheritance(order, labelsWithoutInheritance.isChecked());
        List<FilterPair> result = new ArrayList<>();

        for (FilterPair filterPair : (List<FilterPair>) bdFiltersOrderElement.getSelectedElements()) {
            result.add(toTasKElementFilterEnum(filterPair));
        }

        FilterUtils.writeOrderParameters(order, result);
        FilterUtils.writeOrderWBSFiltersChanged(order, true);
    }

    private FilterPair toTasKElementFilterEnum(FilterPair each) {
        switch ((OrderElementFilterEnum) each.getType()) {

        case Label:
            return new FilterPair(TaskElementFilterEnum.Label, each.getPattern(), each.getValue());

        case Criterion:
            return new FilterPair(TaskElementFilterEnum.Criterion, each.getPattern(), each.getValue());

        default:
            return null;
        }
    }

    private OrderElementPredicate createPredicate() {
        List<FilterPair> listFilters = bdFiltersOrderElement.getSelectedElements();
        Date startDate = filterStartDateOrderElement.getValue();
        Date finishDate = filterFinishDateOrderElement.getValue();
        boolean ignoreLabelsInheritance = labelsWithoutInheritance.isChecked();
        String name = filterNameOrderElement.getValue();

        return listFilters.isEmpty() && startDate == null && finishDate == null && name == null ? null
                : new OrderElementPredicate(listFilters, startDate, finishDate, name, ignoreLabelsInheritance);
    }

    public TreeModel getFilteredTreeModel() {
        OrderElementTreeModel filteredModel = getFilteredModel();

        return filteredModel == null ? null : filteredModel.asTree();
    }

    public OrderElementTreeModel getFilteredModel() {
        if (orderModel == null) {
            return null;
        }

        OrderElementPredicate predicate = createPredicate();
        this.predicate = predicate;

        return predicate != null ? orderModel.getOrderElementsFilteredByPredicate(predicate)
                : orderModel.getOrderElementTreeModel();
    }

    private void filterByPredicate(OrderElementPredicate predicate) {
        OrderElementTreeModel orderElementTreeModel = orderModel.getOrderElementsFilteredByPredicate(predicate);
        tree.setModel(orderElementTreeModel.asTree());
        tree.invalidate();
    }

    public void showAllOrderElements() {
        this.predicate = null;
        tree.setModel(orderModel.getOrderElementTreeModel().asTree());
        tree.invalidate();
    }

    @Override
    protected boolean isNewButtonDisabled() {
        return readOnly;
    }

    /**
     * Clear {@link BandboxSearch} for Labels, and initializes {@link IPredicate}.
     */
    public void clear() {
        selectDefaultTab();
        bdFiltersOrderElement.clear();
        predicate = null;
    }

    private void selectDefaultTab() {
        tabGeneralData.setSelected(true);
    }

    @Override
    protected String createTooltipText(OrderElement elem) {
        StringBuilder tooltipText = new StringBuilder();
        tooltipText.append(elem.getName()).append(". ");

        if ((elem.getDescription() != null) && !("".equals(elem.getDescription()))) {
            tooltipText.append(elem.getDescription());
            tooltipText.append(". ");
        }

        if ((elem.getLabels() != null) && (!elem.getLabels().isEmpty())) {
            tooltipText.append(" ").append(_("Labels")).append(":");
            tooltipText.append(StringUtils.join(elem.getLabels(), ","));
            tooltipText.append(".");
        }

        if ((elem.getCriterionRequirements() != null) && (!elem.getCriterionRequirements().isEmpty())) {
            ArrayList<String> criterionNames = new ArrayList<>();

            for (CriterionRequirement each : elem.getCriterionRequirements()) {

                if (each.isValid()) {
                    criterionNames.add(each.getCriterion().getName());
                }
            }

            if (!criterionNames.isEmpty()) {
                tooltipText.append(" " + _("Criteria") + ":");
                tooltipText.append(StringUtils.join(criterionNames, ","));
                tooltipText.append(".");
            }
        }
        // To calculate other unit advances implement getOtherAdvancesPercentage()
        tooltipText.append(" ").append(_("Progress")).append(":").append(elem.getAdvancePercentage());
        tooltipText.append(".");

        return tooltipText.toString();
    }

    public void showEditionOrderElement(final Treeitem item) {
        OrderElement currentOrderElement = item.getValue();
        markModifiedTreeitem(item.getTreerow());
        IOrderElementModel model = orderModel.getOrderElementModel(currentOrderElement);
        orderElementController.openWindow(model);
        refreshRow(item);
    }

    public void refreshRow(Treeitem item) {
        try {
            getRenderer().updateColumnsFor(item.getValue());
            getRenderer().render(item, item.getValue(), 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Treeitem getTreeitemByOrderElement(OrderElement element) {
        List<Treeitem> listItems = new ArrayList<>(this.tree.getItems());
        for (Treeitem item : listItems) {
            OrderElement orderElement = item.getValue();
            if (orderElement.getId().equals(element.getId())) {
                return item;
            }
        }

        return null;
    }

    /**
     * Operations to filter the orders by multiple filters.
     */
    public Constraint checkConstraintFinishDate() {
        return (comp, value) -> {
            Date finishDate = (Date) value;

            if ((finishDate != null) && (filterStartDateOrderElement.getValue() != null)
                    && (finishDate.compareTo(filterStartDateOrderElement.getValue()) < 0)) {

                filterFinishDateOrderElement.setValue(null);
                throw new WrongValueException(comp, _("must be after start date"));
            }
        };
    }

    public Constraint checkConstraintStartDate() {
        return (comp, value) -> {
            Date startDate = (Date) value;

            if ((startDate != null) && (filterFinishDateOrderElement.getValue() != null)
                    && (startDate.compareTo(filterFinishDateOrderElement.getValue()) > 0)) {

                filterStartDateOrderElement.setValue(null);
                throw new WrongValueException(comp, _("must be lower than end date"));
            }
        };
    }

    @Override
    public void remove(OrderElement element) {
        boolean hasImputedExpenseSheets = orderModel.hasImputedExpenseSheetsThisOrAnyOfItsChildren(element);

        if (hasImputedExpenseSheets) {
            messagesForUser.showMessage(Level.ERROR,
                    _("You can not remove the project \"{0}\" because this one has imputed expense sheets.",
                            element.getName()));
            return;
        }

        boolean alreadyInUse = orderModel.isAlreadyInUse(element);
        if (alreadyInUse) {
            messagesForUser.showMessage(Level.ERROR, _(
                    "You cannot remove the task \"{0}\" because it has work reported on it or any of its children",
                    element.getName()));
            return;
        }

        boolean onlyChildAndParentAlreadyInUseByHoursOrExpenses = orderModel
                .isOnlyChildAndParentAlreadyInUseByHoursOrExpenses(element);

        if (onlyChildAndParentAlreadyInUseByHoursOrExpenses) {
            messagesForUser.showMessage(Level.ERROR,
                    _("You cannot remove the task \"{0}\" because it is the only child of its parent "
                            + "and its parent has tracked time or imputed expenses", element.getName()));
            return;
        }

        super.remove(element);
        getRenderer().removeCodeTextbox(element);
    }

    @Override
    protected IHoursGroupHandler<OrderElement> getHoursGroupHandler() {
        return new IHoursGroupHandler<OrderElement>() {

            @Override
            public boolean hasMoreThanOneHoursGroup(OrderElement element) {
                return element.getHoursGroups().size() > 1;
            }

            @Override
            public boolean isTotalHoursValid(OrderElement line, Integer value) {
                return ((OrderLine) line).isTotalHoursValid(value);
            }

            @Override
            public Integer getWorkHoursFor(OrderElement element) {
                return element.getWorkHours();
            }

            @Override
            public void setWorkHours(OrderElement element, Integer value) {
                if (element instanceof OrderLine) {
                    OrderLine line = (OrderLine) element;
                    line.setWorkHours(value);
                }
            }
        };
    }

    @Override
    protected IBudgetHandler<OrderElement> getBudgetHandler() {
        return new IBudgetHandler<OrderElement>() {

            @Override
            public BigDecimal getBudgetFor(OrderElement element) {
                return element.getBudget();
            }

            @Override
            public void setBudgetHours(OrderElement element, BigDecimal budget) {
                if (element instanceof OrderLine) {
                    OrderLine line = (OrderLine) element;
                    line.setBudget(budget);
                }
            }

        };
    }

    @Override
    protected INameHandler<OrderElement> getNameHandler() {
        return element -> element.getName();
    }

    @Override
    protected ICodeHandler<OrderElement> getCodeHandler() {
        return element -> element.getCode();
    }

}