/*
* 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-2013 Igalia, S.L.
* Copyright (C) 2010-2011 WirelessGalicia S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.planner.order;
import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.web.common.FilterUtils;
import org.libreplan.web.common.ViewSwitcher;
import org.libreplan.web.common.components.bandboxsearch.BandboxMultipleSearch;
import org.libreplan.web.common.components.finders.FilterPair;
import org.libreplan.web.orders.OrderCRUDController;
import org.libreplan.web.planner.TaskElementPredicate;
import org.libreplan.web.planner.advances.AdvanceAssignmentPlanningController;
import org.libreplan.web.planner.calendar.CalendarAllocationController;
import org.libreplan.web.planner.consolidations.AdvanceConsolidationController;
import org.libreplan.web.planner.taskedition.AdvancedAllocationTaskController;
import org.libreplan.web.planner.taskedition.EditTaskController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.zkoss.ganttz.FilterAndParentExpandedPredicates;
import org.zkoss.ganttz.Planner;
import org.zkoss.ganttz.data.Task;
import org.zkoss.ganttz.extensions.ContextWithPlannerTask;
import org.zkoss.ganttz.extensions.ICommand;
import org.zkoss.ganttz.extensions.IContext;
import org.zkoss.ganttz.extensions.IContextWithPlannerTask;
import org.zkoss.ganttz.timetracker.zoom.ZoomLevel;
import org.zkoss.ganttz.util.LongOperationFeedback;
import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation;
import org.zkoss.ganttz.util.ProfilingLogFactory;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Popup;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Vbox;
/**
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class OrderPlanningController implements Composer {
private static final Log PROFILING_LOG = ProfilingLogFactory.getLog(OrderPlanningController.class);
@Autowired
private ViewSwitcher viewSwitcher;
private Map<String, String[]> parameters;
@Autowired
private IOrderPlanningModel model;
private Planner planner;
@Autowired
private CalendarAllocationController calendarAllocationController;
@Autowired
private EditTaskController editTaskController;
@Autowired
private AdvancedAllocationTaskController advancedAllocationTaskController;
@Autowired
private AdvanceConsolidationController advanceConsolidationController;
@Autowired
private AdvanceAssignmentPlanningController advanceAssignmentPlanningController;
@Autowired
private OrderCRUDController orderCRUDController;
private GenericForwardComposer currentControllerToShow;
private Order order;
private TaskElement task;
private List<ICommand<TaskElement>> additional = new ArrayList<>();
private Vbox orderElementFilter;
private Datebox filterStartDateOrderElement;
private Datebox filterFinishDateOrderElement;
private Checkbox labelsWithoutInheritance;
private BandboxMultipleSearch bdFiltersOrderElement;
private Textbox filterNameOrderElement;
private Popup filterOptionsPopup;
public OrderPlanningController() {
}
public List getCriticalPath() {
return planner != null ? planner.getCriticalPath() : null;
}
@SafeVarargs
public final void setOrder(Order order, ICommand<TaskElement>... additionalCommands) {
Validate.notNull(additionalCommands);
Validate.noNullElements(additionalCommands);
this.order = order;
this.additional = Arrays.asList(additionalCommands);
if (planner != null) {
ensureIsInPlanningOrderView();
updateConfiguration();
planner.setTaskListPredicate(getFilterAndParentExpanedPredicates(createPredicate()));
}
}
public void setShowedTask(TaskElement task) {
this.task = task;
}
public CalendarAllocationController getCalendarAllocationController() {
return calendarAllocationController;
}
private void ensureIsInPlanningOrderView() {
viewSwitcher.goToPlanningOrderView();
}
public ViewSwitcher getViewSwitcher() {
return viewSwitcher;
}
@Override
public void doAfterCompose(org.zkoss.zk.ui.Component comp) {
this.planner = (Planner) comp;
String zoomLevelParameter = null;
if ((parameters != null) && (parameters.get("zoom") != null) && !(parameters.isEmpty())) {
zoomLevelParameter = parameters.get("zoom")[0];
}
if (zoomLevelParameter != null) {
planner.setInitialZoomLevel(ZoomLevel.getFromString(zoomLevelParameter));
}
planner.setAreContainersExpandedByDefault(Planner.guessContainersExpandedByDefault(parameters));
planner.setAreShownAdvancesByDefault(Planner.guessShowAdvancesByDefault(parameters));
planner.setAreShownReportedHoursByDefault(Planner.guessShowReportedHoursByDefault(parameters));
planner.setAreShownMoneyCostBarByDefault(Planner.guessShowMoneyCostBarByDefault(parameters));
orderElementFilter = (Vbox) planner.getFellow("orderElementFilter");
// Configuration of the order filter
org.zkoss.zk.ui.Component filterComponent = Executions
.createComponents("/orders/_orderElementTreeFilter.zul",
orderElementFilter, new HashMap<String, String>());
filterComponent.setAttribute("treeController", this, true);
filterOptionsPopup = (Popup) filterComponent.getFellow("filterOptionsPopup");
filterStartDateOrderElement = (Datebox) filterOptionsPopup.getFellow("filterStartDateOrderElement");
filterFinishDateOrderElement = (Datebox) filterOptionsPopup.getFellow("filterFinishDateOrderElement");
labelsWithoutInheritance = (Checkbox) filterOptionsPopup.getFellow("labelsWithoutInheritance");
bdFiltersOrderElement = (BandboxMultipleSearch) filterComponent.getFellow("bdFiltersOrderElement");
bdFiltersOrderElement.setFinder("taskElementsMultipleFiltersFinder");
filterNameOrderElement = (Textbox) filterComponent.getFellow("filterNameOrderElement");
filterComponent.setVisible(true);
updateConfiguration();
}
private void updateConfiguration() {
if (order != null) {
importOrderFiltersFromSession();
long time = System.currentTimeMillis();
model.setConfigurationToPlanner(planner, order, viewSwitcher,
editTaskController, advancedAllocationTaskController,
advanceAssignmentPlanningController,
advanceConsolidationController,
calendarAllocationController, additional);
PROFILING_LOG.debug("setConfigurationToPlanner took: " + (System.currentTimeMillis() - time) + " ms");
planner.updateSelectedZoomLevel();
showResorceAllocationIfIsNeeded();
}
}
private void importOrderFiltersFromSession() {
importOrderFiltersFromSession(false);
}
private void importOrderFiltersFromSession(boolean forceReload) {
filterNameOrderElement.setValue(FilterUtils.readOrderTaskName(order));
filterStartDateOrderElement.setValue(FilterUtils.readOrderStartDate(order));
filterFinishDateOrderElement.setValue(FilterUtils.readOrderEndDate(order));
List<FilterPair> sessionFilterPairs = FilterUtils.readOrderParameters(order);
if ((sessionFilterPairs != null) && (bdFiltersOrderElement.getSelectedElements().isEmpty() || forceReload)) {
bdFiltersOrderElement.addSelectedElements(sessionFilterPairs);
}
if (FilterUtils.readOrderInheritance(order) != null) {
labelsWithoutInheritance.setChecked(FilterUtils.readOrderInheritance(order));
}
}
public EditTaskController getEditTaskController() {
return editTaskController;
}
public AdvancedAllocationTaskController getAdvancedAllocationTaskController() {
return advancedAllocationTaskController;
}
public OrderCRUDController getOrderCRUDController() {
return orderCRUDController;
}
public void setURLParameters(Map<String, String[]> parameters) {
this.parameters = parameters;
}
public Order getOrder() {
return model.getOrder();
}
public void onApplyFilter() {
filterByPredicate(createPredicate());
List<FilterPair> listFilters = (List<FilterPair>) bdFiltersOrderElement.getSelectedElements();
FilterUtils.writeOrderParameters(order, listFilters);
}
private TaskElementPredicate createPredicate() {
if (FilterUtils.hasOrderWBSFiltersChanged(order)) {
importOrderFiltersFromSession(true);
FilterUtils.writeOrderWBSFiltersChanged(order, false);
}
List<FilterPair> listFilters = (List<FilterPair>) bdFiltersOrderElement.getSelectedElements();
Date startDate = filterStartDateOrderElement.getValue();
Date finishDate = filterFinishDateOrderElement.getValue();
boolean ignoreLabelsInheritance = labelsWithoutInheritance.isChecked();
String name = filterNameOrderElement.getValue();
if (listFilters.isEmpty() && startDate == null && finishDate == null && name == null) {
return null;
}
FilterUtils.writeOrderTaskName(order, name);
FilterUtils.writeOrderStartDate(order, startDate);
FilterUtils.writeOrderEndDate(order, finishDate);
FilterUtils.writeOrderInheritance(order, ignoreLabelsInheritance);
return new TaskElementPredicate(listFilters, startDate, finishDate, name, ignoreLabelsInheritance);
}
public Checkbox getLabelsWithoutInheritance() {
return labelsWithoutInheritance;
}
public void setLabelsWithoutInheritance(Checkbox labelsWithoutInheritance) {
this.labelsWithoutInheritance = labelsWithoutInheritance;
}
private void filterByPredicate(final TaskElementPredicate predicate) {
LongOperationFeedback.execute(orderElementFilter, new ILongOperation() {
@Override
public void doAction() {
// FIXME remove or change
model.forceLoadLabelsAndCriterionRequirements();
planner.setTaskListPredicate(getFilterAndParentExpanedPredicates(predicate));
}
@Override
public String getName() {
return _("filtering");
}
});
}
private FilterAndParentExpandedPredicates getFilterAndParentExpanedPredicates(
final TaskElementPredicate predicate) {
final IContext<?> context = planner.getContext();
FilterAndParentExpandedPredicates newPredicate = new FilterAndParentExpandedPredicates(context) {
@Override
public boolean accpetsFilterPredicate(Task task) {
if (predicate == null) {
return true;
}
TaskElement taskElement = (TaskElement) context.getMapper().findAssociatedDomainObject(task);
return predicate.accepts(taskElement);
}
};
newPredicate.setFilterContainers(planner.getPredicate().isFilterContainers());
return newPredicate;
}
public Constraint checkConstraintFinishDate() {
return new Constraint() {
@Override
public void validate(org.zkoss.zk.ui.Component comp, Object value) throws WrongValueException {
Date finishDate = (Date) value;
if ((finishDate != null) && (filterStartDateOrderElement.getValue() != null) &&
(finishDate.compareTo(filterStartDateOrderElement.getValue()) < 0)) {
filterFinishDateOrderElement.setRawValue(null);
throw new WrongValueException(comp, _("must be after start date"));
}
}
};
}
public Constraint checkConstraintStartDate() {
return new Constraint() {
@Override
public void validate(org.zkoss.zk.ui.Component comp, Object value) throws WrongValueException {
Date startDate = (Date) value;
if ((startDate != null) && (filterFinishDateOrderElement.getValue() != null) &&
(startDate.compareTo(filterFinishDateOrderElement.getValue()) > 0)) {
filterStartDateOrderElement.setRawValue(null);
throw new WrongValueException(comp, _("must be lower than end date"));
}
}
};
}
private void showResorceAllocationIfIsNeeded() {
if ((task != null) && (planner != null)) {
planner.expandAllAlways();
Task foundTask = null;
TaskElement foundTaskElement = null;
IContext<TaskElement> context = (IContext<TaskElement>) planner.getContext();
Map<TaskElement, Task> map = context.getMapper().getMapDomainToTask();
for (Entry<TaskElement, Task> entry : map.entrySet()) {
if (task.getId().equals(entry.getKey().getId())) {
foundTaskElement = entry.getKey();
foundTask = entry.getValue();
}
}
if ((foundTask != null) && (foundTaskElement != null)) {
IContextWithPlannerTask<TaskElement> contextTask = ContextWithPlannerTask.create(context, foundTask);
if (this.getCurrentControllerToShow().equals(getEditTaskController())) {
this.editTaskController.showEditFormResourceAllocation(contextTask, foundTaskElement,
model.getPlanningState());
} else if (this.getCurrentControllerToShow().equals(this.getAdvanceAssignmentPlanningController())) {
getAdvanceAssignmentPlanningController().showWindow(contextTask, foundTaskElement,
model.getPlanningState());
}
}
}
}
public AdvanceConsolidationController getAdvanceConsolidationController() {
return advanceConsolidationController;
}
public AdvanceAssignmentPlanningController getAdvanceAssignmentPlanningController() {
return advanceAssignmentPlanningController;
}
public void setCurrentControllerToShow(GenericForwardComposer currentControllerToShow) {
this.currentControllerToShow = currentControllerToShow;
}
private GenericForwardComposer getCurrentControllerToShow() {
return currentControllerToShow;
}
}