/*
GanttProject is an opensource project management tool.
Copyright (C) 2004-2011 GanttProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.sourceforge.ganttproject.task;
import biz.ganttproject.core.calendar.AlwaysWorkingTimeCalendarImpl;
import biz.ganttproject.core.calendar.GPCalendar.DayMask;
import biz.ganttproject.core.calendar.GPCalendar.DayType;
import biz.ganttproject.core.calendar.GPCalendarCalc;
import biz.ganttproject.core.calendar.GPCalendarCalc.MoveDirection;
import biz.ganttproject.core.chart.render.ShapePaint;
import biz.ganttproject.core.time.CalendarFactory;
import biz.ganttproject.core.time.GanttCalendar;
import biz.ganttproject.core.time.TimeDuration;
import biz.ganttproject.core.time.TimeDurationImpl;
import biz.ganttproject.core.time.impl.GPTimeUnitStack;
import com.google.common.collect.ImmutableList;
import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.chart.MilestoneTaskFakeActivity;
import net.sourceforge.ganttproject.document.AbstractURLDocument;
import net.sourceforge.ganttproject.document.Document;
import net.sourceforge.ganttproject.task.algorithm.AlgorithmCollection;
import net.sourceforge.ganttproject.task.algorithm.AlgorithmException;
import net.sourceforge.ganttproject.task.algorithm.CostAlgorithmImpl;
import net.sourceforge.ganttproject.task.algorithm.ShiftTaskTreeAlgorithm;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyException;
import net.sourceforge.ganttproject.task.dependency.TaskDependencySlice;
import net.sourceforge.ganttproject.task.dependency.TaskDependencySliceAsDependant;
import net.sourceforge.ganttproject.task.dependency.TaskDependencySliceAsDependee;
import net.sourceforge.ganttproject.task.dependency.TaskDependencySliceImpl;
import net.sourceforge.ganttproject.task.hierarchy.TaskHierarchyItem;
import net.sourceforge.ganttproject.util.collect.Pair;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* @author bard
*/
public class TaskImpl implements Task {
private final int myID;
private final TaskManagerImpl myManager;
private String myName;
private String myWebLink = "";
private boolean isMilestone;
boolean isProjectTask;
private Priority myPriority;
private GanttCalendar myStart;
private GanttCalendar myEnd;
private GanttCalendar myThird;
private int myThirdDateConstraint;
private int myCompletionPercentage;
private TimeDuration myLength;
private final List<TaskActivity> myActivities = new ArrayList<TaskActivity>();
private boolean bExpand;
// private final TaskDependencyCollection myDependencies = new
// TaskDependencyCollectionImpl();
private final ResourceAssignmentCollectionImpl myAssignments;
private final TaskDependencySlice myDependencySlice;
private final TaskDependencySlice myDependencySliceAsDependant;
private final TaskDependencySlice myDependencySliceAsDependee;
private boolean myEventsEnabled;
private final TaskHierarchyItem myTaskHierarchyItem;
private ShapePaint myShape;
private Color myColor;
private String myNotes;
private MutatorImpl myMutator;
private final CustomColumnsValues customValues;
private boolean critical;
private List<TaskActivity> myMilestoneActivity;
private final CostImpl myCost = new CostImpl();
private boolean isUnplugged = false;
public final static int NONE = 0;
public final static int EARLIESTBEGIN = 1;
private static final GPCalendarCalc RESTLESS_CALENDAR = new AlwaysWorkingTimeCalendarImpl();
private static final TimeDuration EMPTY_DURATION = new TimeDurationImpl(GPTimeUnitStack.DAY, 0);
protected TaskImpl(TaskManagerImpl taskManager, int taskID) {
myManager = taskManager;
myID = taskID;
myAssignments = new ResourceAssignmentCollectionImpl(this, myManager.getConfig().getResourceManager());
myDependencySlice = new TaskDependencySliceImpl(this, myManager.getDependencyCollection(), TaskDependencySlice.COMPLETE_SLICE_FXN);
myDependencySliceAsDependant = new TaskDependencySliceAsDependant(this, myManager.getDependencyCollection());
myDependencySliceAsDependee = new TaskDependencySliceAsDependee(this, myManager.getDependencyCollection());
myPriority = DEFAULT_PRIORITY;
myTaskHierarchyItem = myManager.getHierarchyManager().createItem(this);
myNotes = "";
bExpand = true;
myColor = null;
customValues = new CustomColumnsValues(myManager.getCustomPropertyManager());
}
protected TaskImpl(TaskImpl copy, boolean isUnplugged) {
this.isUnplugged = isUnplugged;
myManager = copy.myManager;
// Use a new (unique) ID for the cloned task
myID = myManager.getAndIncrementId();
if (!isUnplugged) {
myTaskHierarchyItem = myManager.getHierarchyManager().createItem(this);
} else {
myTaskHierarchyItem = copy.myTaskHierarchyItem;
}
myAssignments = new ResourceAssignmentCollectionImpl(this, myManager.getConfig().getResourceManager());
myAssignments.importData(copy.getAssignmentCollection());
myName = copy.myName;
myWebLink = copy.myWebLink;
isMilestone = copy.isMilestone;
isProjectTask = copy.isProjectTask;
myPriority = copy.myPriority;
myStart = copy.myStart;
myEnd = copy.myEnd;
myThird = copy.myThird;
myThirdDateConstraint = copy.myThirdDateConstraint;
myCompletionPercentage = copy.myCompletionPercentage;
myLength = copy.myLength;
myShape = copy.myShape;
myColor = copy.myColor;
myNotes = copy.myNotes;
bExpand = copy.bExpand;
myCost.setValue(copy.myCost);
myDependencySlice = new TaskDependencySliceImpl(this, myManager.getDependencyCollection(), TaskDependencySlice.COMPLETE_SLICE_FXN);
myDependencySliceAsDependant = new TaskDependencySliceAsDependant(this, myManager.getDependencyCollection());
myDependencySliceAsDependee = new TaskDependencySliceAsDependee(this, myManager.getDependencyCollection());
customValues = (CustomColumnsValues) copy.getCustomValues().clone();
recalculateActivities();
}
@Override
public Task unpluggedClone() {
TaskImpl result = new TaskImpl(this, true) {
@Override
public boolean isSupertask() {
return false;
}
};
return result;
}
class MutatorException extends RuntimeException {
public MutatorException(String msg) {
super(msg);
}
}
@Override
public TaskMutator createMutator() {
if (myMutator != null) {
return myMutator;
}
myMutator = new MutatorImpl();
return myMutator;
}
@Override
public TaskMutator createMutatorFixingDuration() {
if (myMutator != null) {
throw new MutatorException("Two mutators have been requested for task=" + getName());
}
myMutator = new MutatorImpl() {
@Override
public void setStart(GanttCalendar start) {
super.setStart(start);
TaskImpl.this.myEnd = null;
}
};
return myMutator;
}
// main properties
@Override
public int getTaskID() {
return myID;
}
@Override
public String getName() {
return myName;
}
public String getWebLink() {
return myWebLink;
}
@Override
public List<Document> getAttachments() {
if (getWebLink() != null && !"".equals(getWebLink())) {
return Collections.singletonList((Document) new AbstractURLDocument() {
@Override
public boolean canRead() {
return true;
}
@Override
public IStatus canWrite() {
return Status.CANCEL_STATUS;
}
@Override
public String getFileName() {
return null;
}
@Override
public InputStream getInputStream() throws IOException {
return null;
}
@Override
public OutputStream getOutputStream() throws IOException {
return null;
}
@Override
public String getPath() {
return null;
}
@Override
public URI getURI() {
try {
return new URI(new URL(getWebLink()).toString());
} catch (URISyntaxException e) {
// Do nothing
} catch (MalformedURLException e) {
File f = new File(getWebLink());
if (f.exists()) {
return f.toURI();
}
}
try {
URL context = myManager.getProjectDocument();
if (context == null) {
return null;
}
URL relative = new URL(context, getWebLink());
return new URI(URLEncoder.encode(relative.toString(), "utf-8"));
} catch (URISyntaxException e) {
// Do nothing
} catch (MalformedURLException e) {
// Do nothing
} catch (UnsupportedEncodingException e) {
// Do nothing
}
return null;
}
@Override
public boolean isLocal() {
return false;
}
@Override
public boolean isValidForMRU() {
return false;
}
@Override
public void write() throws IOException {
}
});
}
return Collections.emptyList();
}
@Override
public boolean isMilestone() {
return isMilestone && Boolean.TRUE == myManager.isZeroMilestones();
}
public boolean isLegacyMilestone() {
return isMilestone;
}
@Override
public Priority getPriority() {
return myPriority;
}
@Override
public GanttCalendar getStart() {
if (myMutator != null && myMutator.myIsolationLevel == TaskMutator.READ_UNCOMMITED) {
return myMutator.getStart();
}
return myStart;
}
@Override
public GanttCalendar getEnd() {
GanttCalendar result = null;
if (myMutator != null && myMutator.myIsolationLevel == TaskMutator.READ_UNCOMMITED) {
result = myMutator.getEnd();
}
if (result == null) {
if (myEnd == null) {
myEnd = calculateEnd();
}
result = myEnd;
}
return result;
}
@Override
public GanttCalendar getDisplayEnd() {
GanttCalendar modelEnd = getEnd();
if (modelEnd.equals(getStart())) {
boolean allMilestones = true;
Task[] deepNestedTasks = getManager().getTaskHierarchy().getDeepNestedTasks(this);
for (Task t : deepNestedTasks) {
if (!t.isSupertask() && !t.isMilestone()) {
allMilestones = false;
break;
}
}
if (!allMilestones) {
GPLogger.getLogger(Task.class).warning(String.format(
"This is probably a bug. Task #%d (%s) has end date=%s equal to start date." +
"It could be possible if all child tasks were milestones, however they are not. Child tasks: %s",
getTaskID(), getName(), modelEnd, Arrays.asList(deepNestedTasks)));
}
return modelEnd;
}
return isMilestone ? modelEnd : modelEnd.getDisplayValue();
}
GanttCalendar calculateEnd() {
GanttCalendar result = getStart().clone();
Date newEnd = shiftDate(result.getTime(), getDuration());
result.setTime(newEnd);
return result;
}
@Override
public GanttCalendar getThird() {
if (myMutator != null && myMutator.myIsolationLevel == TaskMutator.READ_UNCOMMITED) {
return myMutator.getThird();
}
return myThird;
}
@Override
public int getThirdDateConstraint() {
return myThirdDateConstraint;
}
@Override
public List<TaskActivity> getActivities() {
if (isMilestone) {
return myMilestoneActivity;
}
List<TaskActivity> activities = myMutator == null ? null : myMutator.getActivities();
if (activities == null) {
activities = myActivities;
}
return activities;
}
@Override
public TimeDuration getDuration() {
if (isMilestone()) {
return EMPTY_DURATION;
}
return (myMutator != null && myMutator.myIsolationLevel == TaskMutator.READ_UNCOMMITED) ? myMutator.getDuration()
: myLength;
}
@Override
public int getCompletionPercentage() {
return (myMutator != null && myMutator.myIsolationLevel == TaskMutator.READ_UNCOMMITED) ? myMutator.getCompletionPercentage()
: myCompletionPercentage;
}
@Override
public boolean getExpand() {
return bExpand;
}
@Override
public ShapePaint getShape() {
return myShape;
}
@Override
public Color getColor() {
Color result = myColor;
if (result == null) {
if (isMilestone() || myManager.getTaskHierarchy().hasNestedTasks(this)) {
result = Color.BLACK;
} else {
result = myManager.getConfig().getDefaultColor();
}
}
return result;
}
@Override
public String getNotes() {
return myNotes;
}
@Override
public ResourceAssignment[] getAssignments() {
return myAssignments.getAssignments();
}
@Override
public ResourceAssignmentCollection getAssignmentCollection() {
return myAssignments;
}
@Override
public Task getSupertask() {
TaskHierarchyItem container = myTaskHierarchyItem.getContainerItem();
return container.getTask();
}
@Override
public Task[] getNestedTasks() {
TaskHierarchyItem[] nestedItems = myTaskHierarchyItem.getNestedItems();
Task[] result = new Task[nestedItems.length];
for (int i = 0; i < nestedItems.length; i++) {
result[i] = nestedItems[i].getTask();
}
return result;
}
@Override
public void move(Task targetSupertask) {
TaskImpl supertaskImpl = (TaskImpl) targetSupertask;
TaskHierarchyItem targetItem = supertaskImpl.myTaskHierarchyItem;
myTaskHierarchyItem.delete();
targetItem.addNestedItem(myTaskHierarchyItem);
myManager.onTaskMoved(this);
}
@Override
public void delete() {
getDependencies().clear();
getAssignmentCollection().clear();
}
@Override
public TaskDependencySlice getDependencies() {
return myDependencySlice;
}
@Override
public TaskDependencySlice getDependenciesAsDependant() {
return myDependencySliceAsDependant;
}
@Override
public TaskDependencySlice getDependenciesAsDependee() {
return myDependencySliceAsDependee;
}
@Override
public TaskManager getManager() {
return myManager;
}
private static interface EventSender {
void enable();
void fireEvent();
}
private class ProgressEventSender implements EventSender {
private boolean myEnabled;
@Override
public void fireEvent() {
if (myEnabled) {
myManager.fireTaskProgressChanged(TaskImpl.this);
}
myEnabled = false;
}
@Override
public void enable() {
myEnabled = true;
}
}
private class PropertiesEventSender implements EventSender {
private boolean myEnabled;
@Override
public void fireEvent() {
if (myEnabled) {
myManager.fireTaskPropertiesChanged(TaskImpl.this);
}
myEnabled = false;
}
@Override
public void enable() {
myEnabled = true;
}
}
private static class FieldChange {
Object myFieldValue;
Object myOldValue;
EventSender myEventSender;
void setValue(Object newValue) {
myFieldValue = newValue;
myEventSender.enable();
}
public void setOldValue(Object oldValue) {
myOldValue = oldValue;
}
}
private class MutatorImpl implements TaskMutator {
private EventSender myPropertiesEventSender = new PropertiesEventSender();
private EventSender myProgressEventSender = new ProgressEventSender();
private FieldChange myCompletionPercentageChange;
private FieldChange myStartChange;
private FieldChange myEndChange;
private FieldChange myThirdChange;
private FieldChange myDurationChange;
private List<TaskActivity> myActivities;
private Pair<FieldChange, FieldChange> myShiftChange;
private final List<Runnable> myCommands = new ArrayList<Runnable>();
private int myIsolationLevel;
public final Exception myException = new Exception();
@Override
public void commit() {
try {
if (myStartChange != null) {
GanttCalendar start = getStart();
TaskImpl.this.setStart(start);
}
if (myDurationChange != null) {
TimeDuration duration = getDuration();
TaskImpl.this.setDuration(duration);
myEndChange = null;
}
if (myCompletionPercentageChange != null) {
int newValue = getCompletionPercentage();
TaskImpl.this.setCompletionPercentage(newValue);
}
if (myEndChange != null) {
GanttCalendar end = getEnd();
if (end.getTime().compareTo(TaskImpl.this.getStart().getTime()) > 0) {
TaskImpl.this.setEnd(end);
}
}
if (myThirdChange != null) {
GanttCalendar third = getThird();
TaskImpl.this.setThirdDate(third);
}
for (Runnable command : myCommands) {
command.run();
}
myCommands.clear();
myPropertiesEventSender.fireEvent();
myProgressEventSender.fireEvent();
} finally {
TaskImpl.this.myMutator = null;
}
if (myStartChange != null && TaskImpl.this.isSupertask()) {
TaskImpl.this.adjustNestedTasks();
}
if ((myStartChange != null || myEndChange != null || myDurationChange != null || myShiftChange != null || myThirdChange != null) && areEventsEnabled()) {
GanttCalendar oldStart;
if (myStartChange != null) {
oldStart = (GanttCalendar) myStartChange.myOldValue;
} else if (myShiftChange != null) {
oldStart = (GanttCalendar) myShiftChange.first().myOldValue;
} else {
oldStart = TaskImpl.this.getStart();
}
GanttCalendar oldEnd;
if (myEndChange != null) {
oldEnd = (GanttCalendar) myEndChange.myOldValue;
} else if (myShiftChange != null){
oldEnd = (GanttCalendar) myShiftChange.second().myOldValue;
} else {
oldEnd = TaskImpl.this.getEnd();
}
myManager.fireTaskScheduleChanged(TaskImpl.this, oldStart, oldEnd);
}
}
public GanttCalendar getThird() {
return myThirdChange == null ? TaskImpl.this.myThird : (GanttCalendar) myThirdChange.myFieldValue;
}
public List<TaskActivity> getActivities() {
if (myActivities == null && (myStartChange != null) || (myDurationChange != null)) {
myActivities = new ArrayList<TaskActivity>();
TaskImpl.recalculateActivities(myManager.getConfig().getCalendar(), TaskImpl.this, myActivities,
getStart().getTime(), TaskImpl.this.getEnd().getTime());
}
return myActivities;
}
@Override
public void setName(final String name) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setName(name);
}
});
}
@Override
public void setProjectTask(final boolean projectTask) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setProjectTask(projectTask);
}
});
}
@Override
public void setMilestone(final boolean milestone) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setMilestone(milestone);
}
});
}
@Override
public void setPriority(final Priority priority) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setPriority(priority);
}
});
}
@Override
public void setStart(final GanttCalendar start) {
assert start != null;
GanttCalendar currentStart = getStart();
if (currentStart != null && start.equals(currentStart)) {
return;
}
if (myStartChange == null) {
myStartChange = new FieldChange();
myStartChange.myEventSender = myPropertiesEventSender;
}
myStartChange.setOldValue(TaskImpl.this.myStart);
myStartChange.setValue(start);
myActivities = null;
}
@Override
public void setEnd(final GanttCalendar end) {
if (myEndChange == null) {
myEndChange = new FieldChange();
myEndChange.myEventSender = myPropertiesEventSender;
}
myEndChange.setOldValue(TaskImpl.this.myEnd);
myEndChange.setValue(end);
myActivities = null;
}
@Override
public void setThird(final GanttCalendar third, final int thirdDateConstraint) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setThirdDateConstraint(thirdDateConstraint);
}
});
if (myThirdChange == null) {
myThirdChange = new FieldChange();
myThirdChange.myEventSender = myPropertiesEventSender;
}
myThirdChange.setValue(third);
myActivities = null;
}
@Override
public void setDuration(final TimeDuration length) {
// If duration of task was set to 0 or less do not change it
if (length.getLength() <= 0) {
return;
}
if (myDurationChange == null) {
myDurationChange = new FieldChange();
myDurationChange.myEventSender = myPropertiesEventSender;
myDurationChange.setValue(length);
} else {
TimeDuration currentLength = (TimeDuration) myDurationChange.myFieldValue;
if (currentLength.getLength() - length.getLength() == 0) {
return;
}
}
myDurationChange.setValue(length);
Date shifted = TaskImpl.this.shiftDate(getStart().getTime(), length);
GanttCalendar newEnd = CalendarFactory.createGanttCalendar(shifted);
setEnd(newEnd);
myActivities = null;
}
@Override
public void setExpand(final boolean expand) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setExpand(expand);
}
});
}
@Override
public void setCompletionPercentage(final int percentage) {
if (myCompletionPercentageChange == null) {
myCompletionPercentageChange = new FieldChange();
myCompletionPercentageChange.myEventSender = myProgressEventSender;
}
myCompletionPercentageChange.setValue(new Integer(percentage));
}
@Override
public void setCritical(final boolean critical) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setCritical(critical);
}
});
}
@Override
public void setShape(final ShapePaint shape) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setShape(shape);
}
});
}
@Override
public void setColor(final Color color) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setColor(color);
}
});
}
@Override
public void setWebLink(final String webLink) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setWebLink(webLink);
}
});
}
@Override
public void setNotes(final String notes) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.setNotes(notes);
}
});
}
@Override
public void addNotes(final String notes) {
myCommands.add(new Runnable() {
@Override
public void run() {
TaskImpl.this.addNotes(notes);
}
});
}
@Override
public int getCompletionPercentage() {
return myCompletionPercentageChange == null ? TaskImpl.this.myCompletionPercentage
: ((Integer) myCompletionPercentageChange.myFieldValue).intValue();
}
GanttCalendar getStart() {
return myStartChange == null ? TaskImpl.this.myStart : (GanttCalendar) myStartChange.myFieldValue;
}
GanttCalendar getEnd() {
return myEndChange == null ? null : (GanttCalendar) myEndChange.myFieldValue;
}
TimeDuration getDuration() {
return myDurationChange == null ? TaskImpl.this.myLength : (TimeDuration) myDurationChange.myFieldValue;
}
@Override
public void shift(float unitCount) {
Task result = getPrecomputedShift(unitCount);
if (result == null) {
result = TaskImpl.this.shift(unitCount);
cachePrecomputedShift(result, unitCount);
}
setStart(result.getStart());
setDuration(result.getDuration());
setEnd(result.getEnd());
}
@Override
public void shift(TimeDuration shift) {
if (myShiftChange == null) {
myShiftChange = Pair.create(new FieldChange(), new FieldChange());
myShiftChange.first().setOldValue(TaskImpl.this.myStart);
myShiftChange.second().setOldValue(TaskImpl.this.myEnd);
}
ShiftTaskTreeAlgorithm shiftAlgorithm = new ShiftTaskTreeAlgorithm(myManager, null);
try {
shiftAlgorithm.run(TaskImpl.this, shift, ShiftTaskTreeAlgorithm.DEEP);
} catch (AlgorithmException e) {
GPLogger.log(e);
}
}
@Override
public void setIsolationLevel(int level) {
myIsolationLevel = level;
}
private void cachePrecomputedShift(Task result, float unitCount) {
// TODO Implement cache
}
private Task getPrecomputedShift(float unitCount) {
// TODO Use cache to grab value
return null;
}
@Override
public void setTaskInfo(TaskInfo taskInfo) {
myTaskInfo = taskInfo;
}
}
@Override
public void setName(String name) {
myName = (name == null ? null : name.trim());
}
@Override
public void setWebLink(String webLink) {
myWebLink = webLink;
}
@Override
public void setMilestone(boolean milestone) {
isMilestone = milestone;
if (milestone) {
setEnd(null);
}
}
@Override
public void setPriority(Priority priority) {
myPriority = priority;
}
@Override
public void setStart(GanttCalendar start) {
Date closestWorkingStart = myManager.findClosestWorkingTime(start.getTime());
start.setTime(closestWorkingStart);
myStart = start;
recalculateActivities();
adjustNestedTasks();
}
private void adjustNestedTasks() {
assert myManager != null;
try {
AlgorithmCollection algorithmCollection = myManager.getAlgorithmCollection();
if (algorithmCollection != null) {
algorithmCollection.getAdjustTaskBoundsAlgorithm().adjustNestedTasks(this);
}
} catch (TaskDependencyException e) {
if (!GPLogger.log(e)) {
e.printStackTrace(System.err);
}
}
}
@Override
public boolean isSupertask() {
return myManager.getTaskHierarchy().hasNestedTasks(this);
}
@Override
public void setEnd(GanttCalendar end) {
myEnd = end;
recalculateActivities();
}
@Override
public void setThirdDate(GanttCalendar third) {
Date closestWorkingStart = myManager.findClosestWorkingTime(third.getTime());
third.setTime(closestWorkingStart);
myThird = third;
}
@Override
public void setThirdDateConstraint(int thirdDateConstraint) {
myThirdDateConstraint = thirdDateConstraint;
}
@Override
public void shift(TimeDuration shift) {
float unitCount = shift.getLength(myLength.getTimeUnit());
if (unitCount != 0f) {
Task resultTask = shift(unitCount);
GanttCalendar oldStart = myStart;
GanttCalendar oldEnd = myEnd;
myStart = resultTask.getStart();
myLength = resultTask.getDuration();
myEnd = resultTask.getEnd();
if (areEventsEnabled()) {
myManager.fireTaskScheduleChanged(this, oldStart, oldEnd);
}
recalculateActivities();
}
}
public Task shift(float unitCount) {
Task clone = unpluggedClone();
if (unitCount != 0) {
Date newStart;
if (unitCount > 0) {
TimeDuration length = myManager.createLength(myLength.getTimeUnit(), unitCount);
// clone.setDuration(length);
newStart = RESTLESS_CALENDAR.shiftDate(myStart.getTime(), length);
if (0 == (getManager().getCalendar().getDayMask(newStart) & DayMask.WORKING)) {
newStart = getManager().getCalendar().findClosest(newStart, myLength.getTimeUnit(), MoveDirection.FORWARD, DayType.WORKING);
}
} else {
newStart = RESTLESS_CALENDAR.shiftDate(clone.getStart().getTime(),
getManager().createLength(clone.getDuration().getTimeUnit(), (long) unitCount));
if (0 == (getManager().getCalendar().getDayMask(newStart) & DayMask.WORKING)) {
newStart = getManager().getCalendar().findClosest(newStart, myLength.getTimeUnit(), MoveDirection.BACKWARD, DayType.WORKING);
}
}
clone.setStart(CalendarFactory.createGanttCalendar(newStart));
clone.setDuration(myLength);
}
return clone;
}
@Override
public void setDuration(TimeDuration length) {
assert length.getLength() >= 0 : "An attempt to set length=" + length + " to task=" + this;
myLength = length;
myEnd = null;
recalculateActivities();
}
private Date shiftDate(Date input, TimeDuration duration) {
return myManager.getConfig().getCalendar().shiftDate(input, duration);
}
@Override
public TimeDuration translateDuration(TimeDuration duration) {
return myManager.createLength(myLength.getTimeUnit(), translateDurationValue(duration));
}
private float translateDurationValue(TimeDuration duration) {
if (myLength.getTimeUnit().equals(duration.getTimeUnit())) {
return duration.getValue();
}
if (myLength.getTimeUnit().isConstructedFrom(duration.getTimeUnit())) {
return duration.getValue() / myLength.getTimeUnit().getAtomCount(duration.getTimeUnit());
}
if (duration.getTimeUnit().isConstructedFrom(myLength.getTimeUnit())) {
return duration.getValue() * duration.getTimeUnit().getAtomCount(myLength.getTimeUnit());
}
throw new RuntimeException("Can't translate duration=" + duration + " into units=" + myLength.getTimeUnit());
}
private void recalculateActivities() {
if (myLength == null || myManager == null) {
return;
}
if (isMilestone) {
myMilestoneActivity = ImmutableList.<TaskActivity>of(new MilestoneTaskFakeActivity(this));
return;
}
final Date startDate = myStart.getTime();
final Date endDate = getEnd().getTime();
myActivities.clear();
if (startDate.equals(endDate)) {
myActivities.add(new MilestoneTaskFakeActivity(this));
return;
}
recalculateActivities(myManager.getConfig().getCalendar(), this, myActivities, startDate, endDate);
int length = 0;
for (TaskActivity activity : myActivities) {
if (activity.getIntensity() > 0) {
length += activity.getDuration().getLength(getDuration().getTimeUnit());
}
}
myLength = getManager().createLength(myLength.getTimeUnit(), length);
}
private static void recalculateActivities(GPCalendarCalc calendar, Task task, List<TaskActivity> output, Date startDate,
Date endDate) {
TaskActivitiesAlgorithm alg = new TaskActivitiesAlgorithm(calendar);
alg.recalculateActivities(task, output, startDate, endDate);
}
@Override
public void setCompletionPercentage(int percentage) {
if (percentage != myCompletionPercentage) {
myCompletionPercentage = percentage;
EventSender progressEventSender = new ProgressEventSender();
progressEventSender.enable();
progressEventSender.fireEvent();
}
}
@Override
public void setShape(ShapePaint shape) {
myShape = shape;
}
@Override
public void setColor(Color color) {
myColor = color;
}
@Override
public void setNotes(String notes) {
myNotes = notes;
}
@Override
public void setExpand(boolean expand) {
bExpand = expand;
}
@Override
public void addNotes(String notes) {
myNotes += notes;
}
protected void enableEvents(boolean enabled) {
myEventsEnabled = enabled;
}
protected boolean areEventsEnabled() {
return myEventsEnabled && myManager.areEventsEnabled();
}
/**
* Determines whether a special shape is defined for this task.
*
* @return true, if this task has its own shape defined.
*/
public boolean shapeDefined() {
return (myShape != null);
}
/**
* Determines whether a special color is defined for this task.
*
* @return true, if this task has its own color defined.
*/
public boolean colorDefined() {
return (myColor != null);
}
@Override
public String toString() {
return getName();
}
public boolean isUnplugged() {
return this.isUnplugged;
}
/** @return The CustomColumnValues. */
@Override
public CustomColumnsValues getCustomValues() {
return customValues;
}
@Override
public void setCritical(boolean critical) {
this.critical = critical;
}
@Override
public boolean isCritical() {
return this.critical;
}
// TODO: implementation of this method has no correlation with algorithms
// recalculating schedules,
// doesn't affect subtasks and supertasks. It is necessary to call this
// method explicitly from other
// parts of code to be sure that constraint fulfills
@Override
public void applyThirdDateConstraint() {
// if (getThird() != null)
// switch (getThirdDateConstraint()) {
// case EARLIESTBEGIN:
// if (getThird().after(getStart())) {
// shift(myManager.getTimeUnitStack().createDuration(getDuration().getTimeUnit(), getStart().getTime(), getThird().getTime()));
// }
// break;
// }
}
private TaskInfo myTaskInfo;
@Override
public TaskInfo getTaskInfo() {
return myTaskInfo;
}
@Override
public void setTaskInfo(TaskInfo taskInfo) {
myTaskInfo = taskInfo;
}
@Override
public boolean isProjectTask() {
return isProjectTask;
}
@Override
public void setProjectTask(boolean projectTask) {
isProjectTask = projectTask;
}
private class CostImpl implements Cost {
private BigDecimal myValue = BigDecimal.ZERO;
private boolean isCalculated = true;
@Override
public BigDecimal getValue() {
return (isCalculated) ? getCalculatedValue() : getManualValue();
}
@Override
public BigDecimal getManualValue() {
return myValue;
}
@Override
public BigDecimal getCalculatedValue() {
return new CostAlgorithmImpl().getCalculatedCost(TaskImpl.this);
}
@Override
public void setValue(BigDecimal value) {
myValue = value;
}
public void setValue(Cost copy) {
myValue = copy.getValue();
isCalculated = copy.isCalculated();
}
@Override
public boolean isCalculated() {
return isCalculated;
}
@Override
public void setCalculated(boolean calculated) {
isCalculated = calculated;
}
}
@Override
public Cost getCost() {
return myCost;
}
}