/*
* 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.
*
* 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.web.I18nHelper._;
import static org.libreplan.web.resourceload.ResourceLoadModel.asDate;
import static org.libreplan.web.resourceload.ResourceLoadModel.toLocal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.joda.time.LocalDate;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.planner.chart.ILoadChartData;
import org.libreplan.business.planner.chart.ResourceLoadChartData;
import org.libreplan.business.planner.entities.TaskElement;
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.users.entities.User;
import org.libreplan.web.common.FilterUtils;
import org.libreplan.web.common.components.bandboxsearch.BandboxMultipleSearch;
import org.libreplan.web.common.components.finders.FilterPair;
import org.libreplan.web.common.components.finders.ResourceAllocationFilterEnum;
import org.libreplan.web.planner.chart.Chart;
import org.libreplan.web.planner.chart.StandardLoadChartFiller;
import org.libreplan.web.planner.company.CompanyPlanningModel;
import org.libreplan.web.planner.order.BankHolidaysMarker;
import org.libreplan.web.planner.order.IOrderPlanningGate;
import org.libreplan.web.planner.order.PlanningStateCreator;
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
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.zkforge.timeplot.Plotinfo;
import org.zkforge.timeplot.Timeplot;
import org.zkoss.ganttz.IChartVisibilityChangedListener;
import org.zkoss.ganttz.data.resourceload.LoadTimeLine;
import org.zkoss.ganttz.resourceload.IFilterChangedListener;
import org.zkoss.ganttz.resourceload.IPaginationFilterChangedListener;
import org.zkoss.ganttz.resourceload.ISeeScheduledOfListener;
import org.zkoss.ganttz.resourceload.ResourcesLoadPanel;
import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand;
import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.PaginationType;
import org.zkoss.ganttz.timetracker.TimeTracker;
import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener;
import org.zkoss.ganttz.timetracker.zoom.SeveralModifiers;
import org.zkoss.ganttz.timetracker.zoom.ZoomLevel;
import org.zkoss.ganttz.util.Emitter;
import org.zkoss.ganttz.util.Interval;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Tab;
import org.zkoss.zul.Tabbox;
import org.zkoss.zul.Tabpanel;
import org.zkoss.zul.Tabpanels;
import org.zkoss.zul.Tabs;
import org.zkoss.zul.Combobox;
/**
* Controller for Resource Load view ( global and for order ).
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ResourceLoadController implements Composer {
@Autowired
private IResourceLoadModel resourceLoadModel;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private IAdHocTransactionService transactionService;
@Autowired
private IResourcesSearcher resourcesSearcher;
@Autowired
private PlanningStateCreator planningStateCreator;
private List<IToolbarCommand> commands = new ArrayList<>();
private PlanningState filterBy;
private org.zkoss.zk.ui.Component parent;
private Reloader reloader = new Reloader();
private IOrderPlanningGate planningControllerEntryPoints;
private final Runnable onChange = new Runnable() {
public void run() {
reloadWithoutReset();
}
};
public ResourceLoadController() {
}
@Override
public void doAfterCompose(org.zkoss.zk.ui.Component comp) {
this.parent = comp;
}
public void add(IToolbarCommand... commands) {
Validate.noNullElements(commands);
this.commands.addAll(Arrays.asList(commands));
}
public void reload() {
reloader.resetToInitialState();
reloadWithoutReset();
}
public void filterBy(Order order) {
this.filterBy = (order == null) ? null : createPlanningState(order);
}
private static <T> List<T> as(Class<T> klass, Collection<?> entities) {
List<T> result = new ArrayList<>(entities.size());
for (Object each : entities) {
result.add(klass.cast(each));
}
return result;
}
private void reloadWithoutReset() {
transactionService.runOnReadOnlyTransaction(reloader.reload());
}
PlanningState createPlanningState(final Order order) {
return transactionService.runOnReadOnlyTransaction(new IOnTransaction<PlanningState>() {
@Override
public PlanningState execute() {
return planningStateCreator.retrieveOrCreate(parent.getDesktop(), order);
}
});
}
public interface IListenerAdder {
Object addAndReturnListener(ResourcesLoadPanel panel);
}
class LoadChart extends VisualizationModifier implements IListenerAdder {
private Emitter<Timeplot> emitter = Emitter.withInitial(null);
private volatile Chart loadChart;
private IZoomLevelChangedListener zoomLevelListener;
public LoadChart(Runnable onChange, PlanningState filterBy) {
super(onChange, filterBy);
}
@Override
void setup(ResourcesLoadPanel panel) {
panel.setLoadChart(buildChart(emitter));
}
@Override
public Object addAndReturnListener(ResourcesLoadPanel panel) {
IChartVisibilityChangedListener visibilityChangedListener = fillOnChartVisibilityChange();
panel.addChartVisibilityListener(visibilityChangedListener);
return visibilityChangedListener;
}
private IChartVisibilityChangedListener fillOnChartVisibilityChange() {
return new IChartVisibilityChangedListener() {
@Override
public void chartVisibilityChanged(final boolean visible) {
if (visible && loadChart != null) {
loadChart.fillChart();
}
}
};
}
private Tabbox buildChart(Emitter<Timeplot> timePlot) {
Tabbox chartComponent = new Tabbox();
chartComponent.setOrient("vertical");
chartComponent.setHeight("200px");
Tabs chartTabs = new Tabs();
chartTabs.appendChild(new Tab(_("Load")));
chartComponent.appendChild(chartTabs);
chartTabs.setWidth("124px");
Tabpanels chartTabpanels = new Tabpanels();
Tabpanel loadChartPanel = new Tabpanel();
// Avoid adding Timeplot since it has some pending issues
CompanyPlanningModel.appendLoadChartAndLegend(loadChartPanel, timePlot);
chartTabpanels.appendChild(loadChartPanel);
chartComponent.appendChild(chartTabpanels);
return chartComponent;
}
@Override
void updateUI(ResourcesLoadPanel panel, ResourceLoadDisplayData generatedData) {
TimeTracker timeTracker = panel.getTimeTracker();
zoomLevelListener = fillOnZoomChange(panel);
timeTracker.addZoomListener(zoomLevelListener);
Timeplot newLoadChart = buildLoadChart(panel, generatedData, timeTracker);
emitter.emit(newLoadChart);
}
private Timeplot buildLoadChart(
ResourcesLoadPanel resourcesLoadPanel, ResourceLoadDisplayData generatedData, TimeTracker timeTracker) {
Timeplot chartLoadTimeplot = createEmptyTimeplot();
ResourceLoadChartFiller chartFiller = new ResourceLoadChartFiller(generatedData);
loadChart = new Chart(chartLoadTimeplot, chartFiller, timeTracker);
loadChart.setZoomLevel(timeTracker.getDetailLevel());
chartFiller.initializeResources();
if ( resourcesLoadPanel.isVisibleChart() ) {
loadChart.fillChart();
}
return chartLoadTimeplot;
}
private IZoomLevelChangedListener fillOnZoomChange(final ResourcesLoadPanel resourcesLoadPanel) {
return detailLevel -> {
if ( loadChart == null ) {
return;
}
loadChart.setZoomLevel(detailLevel);
if ( resourcesLoadPanel.isVisibleChart() ) {
loadChart.fillChart();
}
adjustZoomPositionScroll(resourcesLoadPanel);
};
}
private void adjustZoomPositionScroll(ResourcesLoadPanel resourcesLoadPanel) {
resourcesLoadPanel.getTimeTrackerComponent().movePositionScroll();
}
private Timeplot createEmptyTimeplot() {
Timeplot timeplot = new Timeplot();
timeplot.appendChild(new Plotinfo());
return timeplot;
}
}
/**
* Some set of widgets that can change the data visualized: filtering, pagination, etc.
*/
private abstract static class VisualizationModifier {
private final Runnable onChange;
private final PlanningState filterBy;
private VisualizationModifier(Runnable onChange, PlanningState filterBy) {
this.onChange = onChange;
this.filterBy = filterBy;
}
protected final void notifyChange() {
onChange.run();
}
protected boolean isAppliedToOrder() {
return filterBy != null;
}
void setup(ResourcesLoadPanel panel) {
}
void checkDependencies() {
}
void applyToParameters(ResourceLoadParameters parameters) {
}
void updateUI(ResourcesLoadPanel panel, ResourceLoadDisplayData generatedData) {
}
}
private abstract static class DependingOnFiltering extends VisualizationModifier {
private final FilterTypeChanger filterType;
private boolean filteringByResource;
DependingOnFiltering(Runnable onChange, PlanningState filterBy, FilterTypeChanger filterType) {
super(onChange, filterBy);
this.filterType = filterType;
this.filteringByResource = filterType.isFilterByResources();
}
public boolean isFilteringByResource() {
return filteringByResource;
}
@Override
void checkDependencies() {
if ( this.filteringByResource != filterType.isFilterByResources() ) {
this.filteringByResource = filterType.isFilterByResources();
filterTypeChanged();
}
}
protected abstract void filterTypeChanged();
}
private static class FilterTypeChanger extends VisualizationModifier implements IListenerAdder {
private boolean filterByResources = true;
private FilterTypeChanger(Runnable onChange, PlanningState filterBy) {
super(onChange, filterBy);
}
public boolean isFilterByResources() {
return filterByResources;
}
@Override
void applyToParameters(ResourceLoadParameters parameters) {
parameters.setFilterByResources(filterByResources);
}
@Override
public Object addAndReturnListener(ResourcesLoadPanel panel) {
IFilterChangedListener listener = new IFilterChangedListener() {
@Override
public void filterChanged(boolean newValue) {
if (filterByResources != newValue) {
filterByResources = newValue;
notifyChange();
}
}
};
panel.addFilterListener(listener);
return listener;
}
}
private static class ByDatesFilter extends VisualizationModifier {
private LocalDate startDateValue;
private LocalDate endDateValue = null;
private final Datebox startBox = new Datebox();
private final Datebox endBox = new Datebox();
private ByDatesFilter(Runnable onChange, PlanningState filterBy, LocalDate startDate, LocalDate endDate) {
super(onChange, filterBy);
startDateValue = (isAppliedToOrder() || (startDate == null))
? null
: startDate.toDateTimeAtStartOfDay().toLocalDate();
endDateValue = (endDate == null) ? null : endDate.toDateTimeAtStartOfDay().toLocalDate();
}
@Override
void setup(ResourcesLoadPanel panel) {
if ( isAppliedToOrder() ) {
return;
}
panel.setFirstOptionalFilter(buildTimeFilter());
}
private Hbox buildTimeFilter() {
startBox.setValue(asDate(startDateValue));
startBox.setWidth("100px");
startBox.addEventListener(Events.ON_CHANGE, event -> {
LocalDate newStart = toLocal(startBox.getValue());
// TODO resolve deprecated
if ( !ObjectUtils.equals(startDateValue, newStart) ) {
startDateValue = newStart;
FilterUtils.writeResourceLoadsStartDate(startDateValue);
notifyChange();
}
});
endBox.setValue(asDate(endDateValue));
endBox.setWidth("100px");
endBox.addEventListener(Events.ON_CHANGE, event -> {
LocalDate newEnd = toLocal(endBox.getValue());
// TODO resolve deprecated
if ( !ObjectUtils.equals(endBox, newEnd) ) {
endDateValue = newEnd;
FilterUtils.writeResourceLoadsEndDate(endDateValue);
notifyChange();
}
});
Hbox hbox = new Hbox();
hbox.appendChild(new Label(_("From") + ":"));
hbox.appendChild(startBox);
hbox.appendChild(new Label(_("To") + ":"));
hbox.appendChild(endBox);
hbox.setAlign("center");
return hbox;
}
@Override
void applyToParameters(ResourceLoadParameters parameters) {
parameters.setInitDateFilter(startDateValue);
parameters.setEndDateFilter(endDateValue);
}
}
private static class ListenerTracker {
private final List<Object> trackedListeners = new ArrayList<>();
public void addListeners(ResourcesLoadPanel panel, Iterable<IListenerAdder> listeners) {
for (IListenerAdder each : listeners) {
Object listener = each.addAndReturnListener(panel);
trackedListeners.add(listener);
}
}
}
private static class WorkersOrCriteriaBandbox extends DependingOnFiltering {
private final BandboxMultipleSearch bandBox = new BandboxMultipleSearch();
private List<Object> entitiesSelected = null;
private final IResourcesSearcher resourcesSearcher;
private Label label = new Label();
private WorkersOrCriteriaBandbox(Runnable onChange,
PlanningState filterBy,
FilterTypeChanger filterType,
IResourcesSearcher resourcesSearcher,
List<FilterPair> selectedFilters) {
super(onChange, filterBy, filterType);
this.resourcesSearcher = resourcesSearcher;
initBandbox();
if ( (selectedFilters != null) && !selectedFilters.isEmpty() ) {
for (FilterPair filterPair : selectedFilters) {
bandBox.addSelectedElement(filterPair);
}
entitiesSelected = getSelected();
}
}
@Override
void setup(ResourcesLoadPanel panel) {
if ( isAppliedToOrder() ) {
return;
}
panel.setSecondOptionalFilter(buildBandboxFilterer());
}
private void initBandbox() {
bandBox.setId("workerBandboxMultipleSearch");
bandBox.setWidthBandbox("185px");
bandBox.setWidthListbox("450px");
bandBox.setFinder(getFinderToUse());
bandBox.afterCompose();
bandBox.addEventListener(Events.ON_CHANGE, event -> {
entitiesSelected = getSelected();
FilterUtils.writeResourceLoadsParameters(bandBox.getSelectedElements());
notifyChange();
});
}
private Hbox buildBandboxFilterer() {
Hbox hbox = new Hbox();
hbox.appendChild(getLabel());
hbox.appendChild(bandBox);
hbox.setAlign("center");
return hbox;
}
private Label getLabel() {
updateLabelValue();
return label;
}
private void updateLabelValue() {
if ( isFilteringByResource() ) {
label.setValue(_("Resources or criteria") + ":");
} else {
label.setValue(_("Criteria") + ":");
}
}
private String getFinderToUse() {
return isFilteringByResource()
? "resourceMultipleFiltersFinderByResourceAndCriterion"
: "criterionMultipleFiltersFinder";
}
@Override
protected void filterTypeChanged() {
if ( isAppliedToOrder() ) {
return;
}
entitiesSelected = null;
bandBox.setFinder(getFinderToUse());
updateLabelValue();
}
@Override
void applyToParameters(ResourceLoadParameters parameters) {
if ( !hasEntitiesSelected() ) {
parameters.clearResourcesToShow();
parameters.clearCriteriaToShow();
} else if ( isFilteringByResource() ) {
parameters.setResourcesToShow(calculateResourcesToShow());
} else {
parameters.setCriteriaToShow(as(Criterion.class, entitiesSelected));
}
}
private List<Resource> calculateResourcesToShow() {
List<Resource> resources = new ArrayList<>();
List<Criterion> criteria = new ArrayList<>();
for (Object each : entitiesSelected) {
if ( each instanceof Resource ) {
resources.add((Resource) each);
} else {
criteria.add((Criterion) each);
}
}
if ( !criteria.isEmpty()) {
resources.addAll(resourcesSearcher.searchBoth().byCriteria(criteria).execute());
}
return resources;
}
public boolean hasEntitiesSelected() {
return entitiesSelected != null && !entitiesSelected.isEmpty();
}
private List<Object> getSelected() {
List<Object> result = new ArrayList<>();
@SuppressWarnings("unchecked")
List<FilterPair> filterPairList = bandBox.getSelectedElements();
for (FilterPair filterPair : filterPairList) {
result.add(filterPair.getValue());
}
return result;
}
}
private static class ByNamePaginator extends DependingOnFiltering implements IListenerAdder {
private static final int ALL = -1;
private final WorkersOrCriteriaBandbox bandbox;
private int currentPosition;
private List<? extends BaseEntity> allEntitiesShown = null;
public ByNamePaginator(Runnable onChange,
PlanningState filterBy,
FilterTypeChanger filterTypeChanger,
WorkersOrCriteriaBandbox bandbox) {
super(onChange, filterBy, filterTypeChanger);
this.bandbox = bandbox;
this.currentPosition = initialPage();
}
private int initialPage() {
return isAppliedToOrder() ? ALL : 0;
}
@Override
public Object addAndReturnListener(ResourcesLoadPanel panel) {
IPaginationFilterChangedListener listener = newPosition -> {
if ( currentPosition != newPosition ) {
currentPosition = newPosition;
notifyChange();
}
};
panel.addPaginationFilterListener(listener);
return listener;
}
@Override
void checkDependencies() {
super.checkDependencies();
if ( bandbox.hasEntitiesSelected() ) {
this.currentPosition = ALL;
}
}
@Override
protected void filterTypeChanged() {
this.currentPosition = 0;
this.allEntitiesShown = null;
}
@Override
void applyToParameters(ResourceLoadParameters parameters) {
parameters.setPageFilterPosition(currentPosition);
}
@Override
void updateUI(ResourcesLoadPanel panel, ResourceLoadDisplayData generatedData) {
panel.setInternalPaginationDisabled(bandbox.hasEntitiesSelected());
Paginator<? extends BaseEntity> paginator = generatedData.getPaginator();
List<? extends BaseEntity> newAllEntities = paginator.getAll();
if ( this.allEntitiesShown == null || !equivalent(this.allEntitiesShown, newAllEntities) ) {
this.currentPosition = initialPage();
this.allEntitiesShown = newAllEntities;
updatePages(
panel.getPaginationFilterCombobox(),
pagesByName(this.allEntitiesShown, paginator.getPageSize()));
}
}
private boolean equivalent(List<? extends BaseEntity> a, List<? extends BaseEntity> b) {
if ( a == null || b == null ) {
return false;
}
if ( a.size() != b.size() ) {
return false;
}
for (int i = 0; i < a.size(); i++) {
BaseEntity aElement = a.get(i);
BaseEntity bElement = b.get(i);
// TODO resolve deprecated
if ( !ObjectUtils.equals(aElement.getId(), bElement.getId()) ) {
return false;
}
}
return true;
}
private void updatePages(Combobox filterByNameCombo, List<Comboitem> pages) {
if ( filterByNameCombo == null ) {
return;
}
filterByNameCombo.getChildren().clear();
Comboitem lastItem = new Comboitem();
lastItem.setLabel(_("All"));
lastItem.setDescription(_("Show all elements"));
lastItem.setValue(ALL);
pages.add(lastItem);
for (Comboitem each : pages) {
filterByNameCombo.appendChild(each);
}
if ( currentPosition >= 0 && currentPosition < pages.size() ) {
filterByNameCombo.setSelectedItem(pages.get(currentPosition));
} else if ( currentPosition == ALL ) {
filterByNameCombo.setSelectedItem(lastItem);
} else {
filterByNameCombo.setSelectedIndex(0);
}
}
private List<Comboitem> pagesByName(List<?> list, int pageSize) {
if ( list.isEmpty() ) {
return new ArrayList<>();
}
return list.get(0) instanceof Resource
? pagesByName(as(Resource.class, list), pageSize, resource -> resource.getName())
: pagesByName(
as(Criterion.class, list),
pageSize,
criterion -> criterion.getType().getName() + ": " + criterion.getName());
}
interface INameExtractor<T> {
String getNameOf(T value);
}
private <T> List<Comboitem> pagesByName(List<T> elements, int pageSize, INameExtractor<T> nameExtractor) {
List<Comboitem> result = new ArrayList<>();
for (int startPos = 0; startPos < elements.size(); startPos += pageSize) {
int endPos = Math.min(startPos + pageSize - 1, elements.size() - 1);
String first = nameExtractor.getNameOf(elements.get(startPos));
String end = nameExtractor.getNameOf(elements.get(endPos));
Comboitem item = buildPageCombo(startPos, first, end);
result.add(item);
}
return result;
}
private Comboitem buildPageCombo(int startPosition, String first, String end) {
Comboitem result = new Comboitem();
result.setLabel(first.substring(0, 1) + " - " + end.substring(0, 1));
result.setDescription(first + " - " + end);
result.setValue(startPosition);
return result;
}
}
private final class Reloader {
private ResourcesLoadPanel resourcesLoadPanel = null;
private ListenerTracker listeners = new ListenerTracker();
private TimeTracker timeTracker;
private IZoomLevelChangedListener zoomLevelListener;
private List<VisualizationModifier> visualizationModifiers = null;
private List<IListenerAdder> listenersToAdd = null;
public Reloader() {
}
private List<VisualizationModifier> getVisualizationModifiers() {
if ( visualizationModifiers != null ) {
return visualizationModifiers;
}
visualizationModifiers = buildVisualizationModifiers();
return visualizationModifiers;
}
private List<VisualizationModifier> buildVisualizationModifiers() {
List<VisualizationModifier> result = new ArrayList<>();
FilterTypeChanger filterTypeChanger = new FilterTypeChanger(onChange, filterBy);
result.add(filterTypeChanger);
// Only by dates and bandbox filter on global resources load
if ( filterBy == null ) {
LocalDate startDate = FilterUtils.readResourceLoadsStartDate();
LocalDate endDate = FilterUtils.readResourceLoadsEndDate();
User user = resourceLoadModel.getUser();
// Calculate filter based on user preferences
if ( user != null ) {
if ( startDate == null && !FilterUtils.hasResourceLoadsStartDateChanged() ) {
if ( user.getResourcesLoadFilterPeriodSince() != null ) {
startDate = new LocalDate().minusMonths(user.getResourcesLoadFilterPeriodSince());
} else {
// Default filter start
startDate = new LocalDate().minusDays(1);
}
}
if ( (endDate == null) &&
!FilterUtils.hasResourceLoadsEndDateChanged() &&
(user.getResourcesLoadFilterPeriodTo() != null) ) {
endDate = new LocalDate().plusMonths(user.getResourcesLoadFilterPeriodTo());
}
}
result.add(new ByDatesFilter(onChange, filterBy, startDate, endDate));
List<FilterPair> filterPairs = FilterUtils.readResourceLoadsBandbox();
if (user != null && (filterPairs == null || filterPairs.isEmpty()) &&
user.getResourcesLoadFilterCriterion() != null) {
filterPairs = new ArrayList<>();
filterPairs.add(new FilterPair(
ResourceAllocationFilterEnum.Criterion,
user.getResourcesLoadFilterCriterion().getFinderPattern(),
user.getResourcesLoadFilterCriterion()));
}
WorkersOrCriteriaBandbox bandbox =
new WorkersOrCriteriaBandbox(onChange, filterBy, filterTypeChanger, resourcesSearcher, filterPairs);
result.add(bandbox);
result.add(new ByNamePaginator(onChange, filterBy, filterTypeChanger, bandbox));
}
result.add(new LoadChart(onChange, filterBy));
return result;
}
private List<IListenerAdder> getListenersToAdd() {
if ( listenersToAdd != null ) {
return listenersToAdd;
}
List<IListenerAdder> result = new ArrayList<>();
for (VisualizationModifier each : getVisualizationModifiers()) {
if ( each instanceof IListenerAdder ) {
result.add((IListenerAdder) each);
}
}
result.add(new GoToScheduleListener());
listenersToAdd = result;
return listenersToAdd;
}
public void resetToInitialState() {
timeTracker = null;
resourcesLoadPanel = null;
listeners = new ListenerTracker();
visualizationModifiers = null;
listenersToAdd = null;
}
public IOnTransaction<Void> reload() {
return () -> {
reloadInTransaction();
return null;
};
}
private void reloadInTransaction() {
for (VisualizationModifier each : getVisualizationModifiers()) {
each.checkDependencies();
}
ResourceLoadParameters parameters = new ResourceLoadParameters(filterBy);
for (VisualizationModifier each : getVisualizationModifiers()) {
each.applyToParameters(parameters);
}
ResourceLoadDisplayData dataToShow = resourceLoadModel.calculateDataToDisplay(parameters);
timeTracker = buildTimeTracker(dataToShow);
if ( resourcesLoadPanel == null ) {
resourcesLoadPanel = buildPanel(dataToShow);
listeners.addListeners(resourcesLoadPanel, getListenersToAdd());
parent.getChildren().clear();
parent.appendChild(resourcesLoadPanel);
for (VisualizationModifier each : getVisualizationModifiers()) {
each.setup(resourcesLoadPanel);
}
} else {
resourcesLoadPanel.init(dataToShow.getLoadTimeLines(), timeTracker);
listeners.addListeners(resourcesLoadPanel, getListenersToAdd());
}
resourcesLoadPanel.afterCompose();
addCommands(resourcesLoadPanel);
for (VisualizationModifier each : getVisualizationModifiers()) {
each.updateUI(resourcesLoadPanel, dataToShow);
}
}
private void addCommands(ResourcesLoadPanel resourcesLoadPanel) {
resourcesLoadPanel.add(commands.toArray(new IToolbarCommand[commands.size()]));
}
private TimeTracker buildTimeTracker(ResourceLoadDisplayData dataToShow) {
ZoomLevel zoomLevel = getZoomLevel(dataToShow);
TimeTracker result = new TimeTracker(
dataToShow.getViewInterval(),
zoomLevel,
SeveralModifiers.create(),
SeveralModifiers.create(createBankHolidaysMarker()),
parent);
setupZoomLevelListener(result);
return result;
}
private BankHolidaysMarker createBankHolidaysMarker() {
return BankHolidaysMarker.create(configurationDAO.getConfiguration().getDefaultCalendar());
}
private ZoomLevel getZoomLevel(ResourceLoadDisplayData dataToShow) {
if ( filterBy != null ) {
Order order = filterBy.getOrder();
ZoomLevel sessionZoom = FilterUtils.readZoomLevel(order);
if ( sessionZoom != null ) {
return sessionZoom;
}
}
ZoomLevel sessionZoom = FilterUtils.readZoomLevelResourcesLoad();
if ( sessionZoom != null ) {
return sessionZoom;
}
return dataToShow.getInitialZoomLevel();
}
private void setupZoomLevelListener(TimeTracker timeTracker) {
zoomLevelListener = getSessionZoomLevelListener();
timeTracker.addZoomListener(zoomLevelListener);
}
private IZoomLevelChangedListener getSessionZoomLevelListener() {
return detailLevel -> {
if ( filterBy != null ) {
Order order = filterBy.getOrder();
FilterUtils.writeZoomLevel(order, detailLevel);
} else {
FilterUtils.writeZoomLevelResourcesLoad(detailLevel);
}
};
}
private ResourcesLoadPanel buildPanel(ResourceLoadDisplayData dataToShow) {
return new ResourcesLoadPanel(
dataToShow.getLoadTimeLines(),
timeTracker,
parent,
resourceLoadModel.isExpandResourceLoadViewCharts(),
PaginationType.EXTERNAL_PAGINATION);
}
}
private class ResourceLoadChartFiller extends StandardLoadChartFiller {
private final ResourceLoadDisplayData generatedData;
private List<Resource> resources;
public ResourceLoadChartFiller(ResourceLoadDisplayData generatedData) {
this.generatedData = generatedData;
}
@Override
protected String getOptionalJavascriptCall() {
return null;
}
@Override
protected ILoadChartData getDataOn(Interval interval) {
return new ResourceLoadChartData(
generatedData.getDayAssignmentsConsidered(), resources, interval.getStart(), interval.getFinish());
}
private void initializeResources() {
resources = generatedData.getResourcesConsidered();
}
}
private class GoToScheduleListener implements IListenerAdder {
@Override
public Object addAndReturnListener(ResourcesLoadPanel panel) {
ISeeScheduledOfListener listener = taskLine -> onSeeScheduleOf(taskLine);
panel.addSeeScheduledOfListener(listener);
return listener;
}
private void onSeeScheduleOf(LoadTimeLine taskLine) {
TaskElement task = (TaskElement) taskLine.getRole().getEntity();
Order order = resourceLoadModel.getOrderByTask(task);
if ( resourceLoadModel.userCanRead(order, SecurityUtils.getSessionUserLoginName()) ) {
if ( order.isScheduled() ) {
planningControllerEntryPoints.goToTaskResourceAllocation(order, task);
} else {
Messagebox.show(
_("The project has no scheduled elements"), _("Information"),
Messagebox.OK, Messagebox.INFORMATION);
}
} else {
Messagebox.show(
_("You don't have read access to this project"), _("Information"),
Messagebox.OK, Messagebox.INFORMATION);
}
}
}
public void setPlanningControllerEntryPoints(IOrderPlanningGate planningControllerEntryPoints) {
this.planningControllerEntryPoints = planningControllerEntryPoints;
}
public IOrderPlanningGate getPlanningControllerEntryPoints() {
return this.planningControllerEntryPoints;
}
}