/*
* 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-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zkoss.ganttz.data;
import static org.zkoss.ganttz.data.constraint.ConstraintOnComparableValues.biggerOrEqualThan;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.Validate;
import org.joda.time.Duration;
import org.joda.time.LocalDate;
import org.joda.time.ReadableDuration;
import org.zkoss.ganttz.ProjectStatusEnum;
import org.zkoss.ganttz.data.GanttDiagramGraph.IDependenciesEnforcerHook;
import org.zkoss.ganttz.data.GanttDiagramGraph.IDependenciesEnforcerHookFactory;
import org.zkoss.ganttz.data.GanttDiagramGraph.INotificationAfterDependenciesEnforcement;
import org.zkoss.ganttz.data.constraint.Constraint;
import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener;
import org.zkoss.ganttz.extensions.IContextWithPlannerTask;
import org.zkoss.ganttz.util.ConstraintViolationNotificator;
import org.zkoss.ganttz.util.WeakReferencedListeners.Mode;
/**
* This class contains the information of a task. It can be modified and
* notifies of the changes to the interested parties. <br/>
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
public abstract class Task implements ITaskFundamentalProperties {
public interface IReloadResourcesTextRequested {
void reloadResourcesTextRequested();
}
private List<IReloadResourcesTextRequested> reloadRequestedListeners = new ArrayList<>();
private PropertyChangeSupport fundamentalPropertiesListeners = new PropertyChangeSupport(this);
private PropertyChangeSupport visibilityProperties = new PropertyChangeSupport(this);
private PropertyChangeSupport criticalPathProperty = new PropertyChangeSupport(this);
private PropertyChangeSupport advancesProperty = new PropertyChangeSupport(this);
private PropertyChangeSupport reportedHoursProperty = new PropertyChangeSupport(this);
private PropertyChangeSupport moneyCostBarProperty = new PropertyChangeSupport(this);
private final ITaskFundamentalProperties fundamentalProperties;
private boolean visible = true;
private boolean inCriticalPath = false;
private boolean showingAdvances = false;
private boolean showingReportedHours = false;
private boolean showingMoneyCostBar = false;
private ConstraintViolationNotificator<GanttDate> violationNotificator = ConstraintViolationNotificator.create();
private IDependenciesEnforcerHook dependenciesEnforcerHook = GanttDiagramGraph.doNothingHook();
private final INotificationAfterDependenciesEnforcement notifyDates =
new INotificationAfterDependenciesEnforcement() {
@Override
public void onStartDateChange(GanttDate previousStart, GanttDate previousEnd, GanttDate newStart) {
fundamentalPropertiesListeners.firePropertyChange(
"beginDate", previousStart, fundamentalProperties.getBeginDate());
fireEndDate(previousEnd);
}
@Override
public void onEndDateChange(GanttDate previousEnd, GanttDate newEnd) {
fireEndDate(previousEnd);
}
private void fireEndDate(GanttDate previousEnd) {
fundamentalPropertiesListeners.firePropertyChange(
"endDate", previousEnd, fundamentalProperties.getEndDate());
}
};
public Task(ITaskFundamentalProperties fundamentalProperties) {
this.fundamentalProperties = fundamentalProperties;
}
@Override
public List<Constraint<GanttDate>> getStartConstraints() {
return violationNotificator.withListener(fundamentalProperties.getStartConstraints());
}
public List<Constraint<GanttDate>> getEndConstraints() {
return violationNotificator.withListener(fundamentalProperties.getEndConstraints());
}
@Override
public void doPositionModifications(final IModifications modifications) {
fundamentalProperties.doPositionModifications(p -> modifications.doIt(position));
}
private final IUpdatablePosition position = new IUpdatablePosition() {
@Override
public void setEndDate(GanttDate value) {
if ( value == null ) {
return;
}
GanttDate previousEnd = fundamentalProperties.getEndDate();
getFundamentalPropertiesPosition().setEndDate(value);
dependenciesEnforcerHook.setNewEnd(previousEnd, fundamentalProperties.getEndDate());
}
@Override
public void setBeginDate(GanttDate newStart) {
GanttDate previousValue = fundamentalProperties.getBeginDate();
GanttDate previousEnd = fundamentalProperties.getEndDate();
getFundamentalPropertiesPosition().setBeginDate(newStart);
dependenciesEnforcerHook.setStartDate(previousValue, previousEnd, newStart);
}
@Override
public void resizeTo(GanttDate newEnd) {
GanttDate previousEnd = getEndDate();
getFundamentalPropertiesPosition().resizeTo(newEnd);
dependenciesEnforcerHook.setNewEnd(previousEnd, newEnd);
}
@Override
public void moveTo(GanttDate date) {
GanttDate previousStart = getBeginDate();
GanttDate previousEnd = getEndDate();
getFundamentalPropertiesPosition().moveTo(date);
dependenciesEnforcerHook.setStartDate(previousStart, previousEnd, date);
}
private IUpdatablePosition getFundamentalPropertiesPosition() {
final IUpdatablePosition[] result = new IUpdatablePosition[1];
fundamentalProperties.doPositionModifications(position1 -> result[0] = position1);
return result[0];
}
};
public abstract boolean isLeaf();
public abstract boolean isContainer();
public abstract boolean isExpanded() throws UnsupportedOperationException;
public abstract List<Task> getTasks() throws UnsupportedOperationException;
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
boolean previousValue = this.visible;
this.visible = visible;
visibilityProperties.firePropertyChange("visible", previousValue, this.visible);
}
public boolean isInCriticalPath() {
return inCriticalPath;
}
public void setInCriticalPath(boolean inCriticalPath) {
boolean previousValue = this.inCriticalPath;
this.inCriticalPath = inCriticalPath;
criticalPathProperty.firePropertyChange("inCriticalPath", previousValue, this.inCriticalPath);
}
public void setShowingAdvances(boolean showingAdvances) {
boolean previousValue = this.showingAdvances;
this.showingAdvances = showingAdvances;
advancesProperty.firePropertyChange("showingAdvances", previousValue, this.showingAdvances);
}
public boolean isShowingAdvances() {
return showingAdvances;
}
public void setShowingReportedHours(boolean showingReportedHours) {
boolean previousValue = this.showingReportedHours;
this.showingReportedHours = showingReportedHours;
reportedHoursProperty.firePropertyChange("showingReportedHours", previousValue, this.showingReportedHours);
}
public boolean isShowingReportedHours() {
return showingReportedHours;
}
public void setShowingMoneyCostBar(boolean showingMoneyCostBar) {
boolean previousValue = this.showingMoneyCostBar;
this.showingMoneyCostBar = showingMoneyCostBar;
moneyCostBarProperty.firePropertyChange("showingMoneyCostBar", previousValue, this.showingMoneyCostBar);
}
public boolean isShowingMoneyCostBar() {
return showingMoneyCostBar;
}
public String getName() {
return fundamentalProperties.getName();
}
public void setName(String name) {
String previousValue = fundamentalProperties.getName();
fundamentalProperties.setName(name);
fundamentalPropertiesListeners.firePropertyChange("name", previousValue, name);
}
public void registerDependenciesEnforcerHook(IDependenciesEnforcerHookFactory<Task> factory) {
Validate.notNull(factory);
dependenciesEnforcerHook = factory.create(this, notifyDates);
Validate.notNull(dependenciesEnforcerHook);
}
public void enforceDependenciesDueToPositionPotentiallyModified() {
dependenciesEnforcerHook.positionPotentiallyModified();
}
public GanttDate getBeginDate() {
return fundamentalProperties.getBeginDate();
}
public long getLengthMilliseconds() {
return getEndDate().toDayRoundedDate().getTime() - getBeginDate().toDayRoundedDate().getTime();
}
public ReadableDuration getLength() {
return new Duration(getBeginDate().toDayRoundedDate().getTime(), getEndDate().toDayRoundedDate().getTime());
}
public void addVisibilityPropertiesChangeListener(PropertyChangeListener listener) {
this.visibilityProperties.addPropertyChangeListener(listener);
}
public void addCriticalPathPropertyChangeListener(PropertyChangeListener listener) {
this.criticalPathProperty.addPropertyChangeListener(listener);
}
public void addAdvancesPropertyChangeListener(PropertyChangeListener listener) {
this.advancesProperty.addPropertyChangeListener(listener);
}
public void addReportedHoursPropertyChangeListener(PropertyChangeListener listener) {
this.reportedHoursProperty.addPropertyChangeListener(listener);
}
public void addMoneyCostBarPropertyChangeListener(PropertyChangeListener listener) {
this.moneyCostBarProperty.addPropertyChangeListener(listener);
}
public void addFundamentalPropertiesChangeListener(PropertyChangeListener listener) {
this.fundamentalPropertiesListeners.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.fundamentalPropertiesListeners.removePropertyChangeListener(listener);
}
public void removeVisibilityPropertiesChangeListener(PropertyChangeListener listener) {
this.visibilityProperties.removePropertyChangeListener(listener);
}
@Override
public GanttDate getEndDate() {
return fundamentalProperties.getEndDate();
}
@Override
public List<Constraint<GanttDate>> getCurrentLengthConstraint() {
if ( isContainer() ) {
return Collections.emptyList();
}
return violationNotificator.withListener(fundamentalProperties.getCurrentLengthConstraint());
}
public Constraint<GanttDate> getEndDateBiggerThanStartDate() {
return violationNotificator.withListener(biggerOrEqualThan(getBeginDate()));
}
public String getNotes() {
return fundamentalProperties.getNotes();
}
public void setNotes(String notes) {
String previousValue = fundamentalProperties.getNotes();
this.fundamentalProperties.setNotes(notes);
fundamentalPropertiesListeners.firePropertyChange(
"notes", previousValue, this.fundamentalProperties.getNotes());
}
public void resizeTo(final LocalDate date) {
if ( date.compareTo(getBeginDateAsLocalDate()) < 0 ) {
return;
}
doPositionModifications(position1 -> position1.resizeTo(GanttDate.createFrom(date)));
}
public void removed() {
setVisible(false);
}
@Override
public BigDecimal getHoursAdvanceBarPercentage() {
return fundamentalProperties.getHoursAdvanceBarPercentage();
}
@Override
public BigDecimal getAdvancePercentage() {
return fundamentalProperties.getAdvancePercentage();
}
@Override
public GanttDate getHoursAdvanceBarEndDate() {
return fundamentalProperties.getHoursAdvanceBarEndDate();
}
@Override
public GanttDate getMoneyCostBarEndDate() {
return fundamentalProperties.getMoneyCostBarEndDate();
}
@Override
public BigDecimal getMoneyCostBarPercentage() {
return fundamentalProperties.getMoneyCostBarPercentage();
}
@Override
public GanttDate getAdvanceBarEndDate() {
return fundamentalProperties.getAdvanceBarEndDate();
}
public GanttDate getAdvanceBarEndDate(String progressType) {
return fundamentalProperties.getAdvanceBarEndDate(progressType);
}
public String getTooltipText() {
return fundamentalProperties.getTooltipText();
}
public String updateTooltipText() {
return fundamentalProperties.updateTooltipText();
}
public String updateTooltipText(String progressType) {
return fundamentalProperties.updateTooltipText(progressType);
}
public String getLabelsText() {
return fundamentalProperties.getLabelsText();
}
public String getResourcesText() {
return fundamentalProperties.getResourcesText();
}
@Override
public Date getDeadline() {
return fundamentalProperties.getDeadline();
}
@Override
public void setDeadline(Date date) {
Date previousValue = fundamentalProperties.getDeadline();
fundamentalProperties.setDeadline(date);
fundamentalPropertiesListeners.firePropertyChange("deadline", previousValue, date);
}
@Override
public GanttDate getConsolidatedline() {
return fundamentalProperties.getConsolidatedline();
}
public void addConstraintViolationListener(IConstraintViolationListener<GanttDate> listener, Mode mode) {
violationNotificator.addConstraintViolationListener(listener, mode);
}
public void addReloadListener(IReloadResourcesTextRequested reloadResourcesTextRequested) {
Validate.notNull(reloadResourcesTextRequested);
this.reloadRequestedListeners.add(reloadResourcesTextRequested);
}
public void removeReloadListener(IReloadResourcesTextRequested reloadResourcesTextRequested) {
this.reloadRequestedListeners.remove(reloadResourcesTextRequested);
}
public void reloadResourcesText() {
for (IReloadResourcesTextRequested each : reloadRequestedListeners) {
each.reloadResourcesTextRequested();
}
}
public static void reloadResourcesText(IContextWithPlannerTask<?> context) {
Task task = context.getTask();
task.reloadResourcesText();
List<? extends TaskContainer> parents = context.getMapper().getParents(task);
for (TaskContainer each : parents) {
each.reloadResourcesText();
}
}
public boolean isSubcontracted() {
return fundamentalProperties.isSubcontracted();
}
public boolean isLimiting() {
return fundamentalProperties.isLimiting();
}
public boolean isLimitingAndHasDayAssignments() {
return fundamentalProperties.isLimitingAndHasDayAssignments();
}
public boolean hasConsolidations() {
return fundamentalProperties.hasConsolidations();
}
public boolean canBeExplicitlyResized() {
return fundamentalProperties.canBeExplicitlyResized();
}
public abstract boolean canBeExplicitlyMoved();
@Override
public String toString() {
return fundamentalProperties.getName();
}
public List<Task> getAllTaskLeafs() {
return Arrays.asList(this);
}
public String getAssignedStatus() {
return fundamentalProperties.getAssignedStatus();
}
public boolean isFixed() {
return fundamentalProperties.isFixed();
}
public LocalDate getBeginDateAsLocalDate() {
return LocalDate.fromDateFields(getBeginDate().toDayRoundedDate());
}
@Override
public boolean isManualAnyAllocation() {
return fundamentalProperties.isManualAnyAllocation();
}
public boolean belongsClosedProject() {
return fundamentalProperties.belongsClosedProject();
}
public boolean isRoot() {
return fundamentalProperties.isRoot();
}
public void updateSizeDueToDateChanges(GanttDate previousStart, GanttDate previousEnd) {
dependenciesEnforcerHook.setStartDate(previousStart, previousEnd, getBeginDate());
}
@Override
public boolean isUpdatedFromTimesheets() {
return fundamentalProperties.isUpdatedFromTimesheets();
}
@Override
public Date getFirstTimesheetDate() {
return fundamentalProperties.getFirstTimesheetDate();
}
@Override
public Date getLastTimesheetDate() {
return fundamentalProperties.getLastTimesheetDate();
}
public void firePropertyChangeForTaskDates() {
fundamentalPropertiesListeners.firePropertyChange("beginDate", null, getBeginDate());
fundamentalPropertiesListeners.firePropertyChange("endDate", null, getEndDate());
}
public String getCode() {
return fundamentalProperties.getCode();
}
public String getProjectCode() {
return fundamentalProperties.getProjectCode();
}
public ProjectStatusEnum getProjectHoursStatus() {
return fundamentalProperties.getProjectHoursStatus();
}
public ProjectStatusEnum getProjectBudgetStatus() {
return fundamentalProperties.getProjectBudgetStatus();
}
public String getTooltipTextForProjectHoursStatus() {
return fundamentalProperties.getTooltipTextForProjectHoursStatus();
}
public String getTooltipTextForProjectBudgetStatus() {
return fundamentalProperties.getTooltipTextForProjectBudgetStatus();
}
}