/*
* Created on 05.07.2003
*
*/
package net.sourceforge.ganttproject.task;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Comparator;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import biz.ganttproject.core.calendar.AlwaysWorkingTimeCalendarImpl;
import biz.ganttproject.core.calendar.GPCalendarCalc;
import biz.ganttproject.core.calendar.GPCalendarListener;
import biz.ganttproject.core.chart.scene.BarChartActivity;
import biz.ganttproject.core.chart.scene.gantt.ChartBoundsAlgorithm;
import biz.ganttproject.core.chart.scene.gantt.ChartBoundsAlgorithm.Result;
import biz.ganttproject.core.option.DefaultEnumerationOption;
import biz.ganttproject.core.option.DefaultStringOption;
import biz.ganttproject.core.option.EnumerationOption;
import biz.ganttproject.core.option.StringOption;
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.TimeUnit;
import biz.ganttproject.core.time.TimeUnitStack;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import net.sourceforge.ganttproject.CustomPropertyDefinition;
import net.sourceforge.ganttproject.CustomPropertyListener;
import net.sourceforge.ganttproject.CustomPropertyManager;
import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.GanttTask;
import net.sourceforge.ganttproject.ProjectEventListener;
import net.sourceforge.ganttproject.gui.NotificationChannel;
import net.sourceforge.ganttproject.gui.NotificationItem;
import net.sourceforge.ganttproject.gui.NotificationManager;
import net.sourceforge.ganttproject.gui.options.model.GP1XOptionConverter;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.resource.HumanResource;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.task.algorithm.AdjustTaskBoundsAlgorithm;
import net.sourceforge.ganttproject.task.algorithm.AlgorithmCollection;
import net.sourceforge.ganttproject.task.algorithm.CriticalPathAlgorithm;
import net.sourceforge.ganttproject.task.algorithm.CriticalPathAlgorithmImpl;
import net.sourceforge.ganttproject.task.algorithm.DependencyGraph;
import net.sourceforge.ganttproject.task.algorithm.FindPossibleDependeesAlgorithm;
import net.sourceforge.ganttproject.task.algorithm.FindPossibleDependeesAlgorithmImpl;
import net.sourceforge.ganttproject.task.algorithm.RecalculateTaskCompletionPercentageAlgorithm;
import net.sourceforge.ganttproject.task.algorithm.RecalculateTaskScheduleAlgorithm;
import net.sourceforge.ganttproject.task.algorithm.SchedulerImpl;
import net.sourceforge.ganttproject.task.dependency.EventDispatcher;
import net.sourceforge.ganttproject.task.dependency.TaskDependency;
import net.sourceforge.ganttproject.task.dependency.TaskDependency.Hardness;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyCollection;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyCollectionImpl;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyConstraint;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyException;
import net.sourceforge.ganttproject.task.dependency.constraint.FinishFinishConstraintImpl;
import net.sourceforge.ganttproject.task.dependency.constraint.FinishStartConstraintImpl;
import net.sourceforge.ganttproject.task.dependency.constraint.StartFinishConstraintImpl;
import net.sourceforge.ganttproject.task.dependency.constraint.StartStartConstraintImpl;
import net.sourceforge.ganttproject.task.event.TaskDependencyEvent;
import net.sourceforge.ganttproject.task.event.TaskHierarchyEvent;
import net.sourceforge.ganttproject.task.event.TaskListener;
import net.sourceforge.ganttproject.task.event.TaskPropertyEvent;
import net.sourceforge.ganttproject.task.event.TaskScheduleEvent;
import net.sourceforge.ganttproject.task.hierarchy.TaskHierarchyManagerImpl;
import net.sourceforge.ganttproject.util.collect.Pair;
/**
* @author bard
*/
public class TaskManagerImpl implements TaskManager {
private static final GPCalendarCalc RESTLESS_CALENDAR = new AlwaysWorkingTimeCalendarImpl();
private final TaskHierarchyManagerImpl myHierarchyManager;
private final TaskDependencyCollectionImpl myDependencyCollection;
private final AlgorithmCollection myAlgorithmCollection;
private final List<TaskListener> myListeners = new ArrayList<TaskListener>();
private AtomicInteger myMaxID = new AtomicInteger(0);
private final Task myRoot;
private final TaskManagerConfig myConfig;
private final TaskNamePrefixOption myTaskNamePrefixOption = new TaskNamePrefixOption();
private final StringOption myTaskCopyNamePrefixOption = new DefaultStringOption("taskCopyNamePrefix", GanttLanguage.getInstance().getText("task.copy.prefix"));
private final EnumerationOption myDependencyHardnessOption = new DefaultEnumerationOption<Object>(
"dependencyDefaultHardness", new String[] { "Strong", "Rubber" }) {
{
resetValue("Strong", true);
}
};
private final TaskContainmentHierarchyFacade.Factory myFacadeFactory;
private final Supplier<TaskContainmentHierarchyFacade> myHierarchySupplier = new Supplier<TaskContainmentHierarchyFacade>() {
@Override
public TaskContainmentHierarchyFacade get() {
return getTaskHierarchy();
}
};
private final DependencyGraph myDependencyGraph = new DependencyGraph(myHierarchySupplier, new DependencyGraph.Logger() {
@Override
public void logDependencyLoop(String title, String message) {
if (myConfig.getNotificationManager() != null) {
myConfig.getNotificationManager().addNotifications(NotificationChannel.WARNING,
ImmutableList.of(new NotificationItem("Dependency loop detected", message, NotificationManager.DEFAULT_HYPERLINK_LISTENER)));
}
GPLogger.log(title + "\n" + message);
}
});
private final SchedulerImpl myScheduler = new SchedulerImpl(myDependencyGraph, myHierarchySupplier);
private boolean areEventsEnabled = true;
private static class TaskMap {
private final Map<Integer, Task> myId2task = new HashMap<Integer, Task>();
private TaskDocumentOrderComparator myComparator;
private boolean isModified = true;
private Task[] myArray;
private final TaskManagerImpl myManager;
TaskMap(TaskManagerImpl taskManager) {
myComparator = new TaskDocumentOrderComparator(taskManager);
myManager = taskManager;
}
void addTask(Task task) {
myId2task.put(new Integer(task.getTaskID()), task);
isModified = true;
}
Task getTask(int id) {
return myId2task.get(new Integer(id));
}
public Task[] getTasks() {
if (isModified) {
myArray = myId2task.values().toArray(new Task[myId2task.size()]);
Arrays.sort(myArray, myComparator);
isModified = false;
}
return myArray;
}
public void clear() {
myId2task.clear();
isModified = true;
}
public void removeTask(Task task) {
myId2task.remove(new Integer(task.getTaskID()));
Task[] nestedTasks = myManager.getTaskHierarchy().getNestedTasks(task);
for (int i = 0; i < nestedTasks.length; i++) {
removeTask(nestedTasks[i]);
}
isModified = true;
}
public int size() {
return myId2task.size();
}
public boolean isEmpty() {
return myId2task.isEmpty();
}
void setDirty() {
isModified = true;
}
}
private final TaskMap myTaskMap = new TaskMap(this);
private final CustomPropertyListenerImpl myCustomPropertyListener;
private final CustomColumnsManager myCustomColumnsManager;
private Boolean isZeroMilestones = true;
TaskManagerImpl(TaskContainmentHierarchyFacade.Factory containmentFacadeFactory, TaskManagerConfig config) {
myCustomPropertyListener = new CustomPropertyListenerImpl(this);
myCustomColumnsManager = new CustomColumnsManager();
myCustomColumnsManager.addListener(getCustomPropertyListener());
myConfig = config;
myHierarchyManager = new TaskHierarchyManagerImpl();
EventDispatcher dispatcher = new EventDispatcher() {
@Override
public void fireDependencyAdded(TaskDependency dep) {
TaskManagerImpl.this.fireDependencyAdded(dep);
}
@Override
public void fireDependencyRemoved(TaskDependency dep) {
TaskManagerImpl.this.fireDependencyRemoved(dep);
}
@Override
public void fireDependencyChanged(TaskDependency dep) {
TaskManagerImpl.this.fireDependencyChanged(dep);
}
};
myDependencyCollection = new TaskDependencyCollectionImpl(containmentFacadeFactory, dispatcher) {
@Override
protected TaskContainmentHierarchyFacade getTaskHierarchy() {
return TaskManagerImpl.this.getTaskHierarchy();
}
@Override
protected Hardness getDefaultHardness() {
String optionValue = getDependencyHardnessOption().getValue();
return optionValue == null ? super.getDefaultHardness() : TaskDependency.Hardness.parse(optionValue);
}
};
myFacadeFactory = containmentFacadeFactory == null ? new FacadeFactoryImpl() : containmentFacadeFactory;
// clear();
myRoot = createRootTask();
FindPossibleDependeesAlgorithm alg1 = new FindPossibleDependeesAlgorithmImpl() {
@Override
protected TaskContainmentHierarchyFacade createContainmentFacade() {
return TaskManagerImpl.this.getTaskHierarchy();
}
};
AdjustTaskBoundsAlgorithm alg3 = new AdjustTaskBoundsAlgorithm() {
@Override
protected TaskContainmentHierarchyFacade createContainmentFacade() {
return TaskManagerImpl.this.getTaskHierarchy();
}
};
RecalculateTaskScheduleAlgorithm alg2 = new RecalculateTaskScheduleAlgorithm(alg3) {
@Override
protected TaskContainmentHierarchyFacade createContainmentFacade() {
return TaskManagerImpl.this.getTaskHierarchy();
}
};
RecalculateTaskCompletionPercentageAlgorithm alg4 = new RecalculateTaskCompletionPercentageAlgorithm() {
@Override
protected TaskContainmentHierarchyFacade createContainmentFacade() {
return TaskManagerImpl.this.getTaskHierarchy();
}
};
ChartBoundsAlgorithm alg5 = new ChartBoundsAlgorithm();
CriticalPathAlgorithm alg6 = new CriticalPathAlgorithmImpl(this, getCalendar());
myAlgorithmCollection = new AlgorithmCollection(this, alg1, alg2, alg3, alg4, alg5, alg6, myScheduler);
addTaskListener(myScheduler.getTaskModelListener());
}
private CustomPropertyListener getCustomPropertyListener() {
return myCustomPropertyListener;
}
@Override
public GanttTask getTask(int taskId) {
return (GanttTask) myTaskMap.getTask(taskId);
}
@Override
public Task getRootTask() {
return myRoot;
}
@Override
public Task[] getTasks() {
return myTaskMap.getTasks();
}
private Task createRootTask() {
Calendar c = CalendarFactory.newCalendar();
Date today = c.getTime();
Task root = new GanttTask(null, CalendarFactory.createGanttCalendar(today), 1, this, -1);
root.setStart(CalendarFactory.createGanttCalendar(today));
root.setDuration(createLength(getConfig().getTimeUnitStack().getDefaultTimeUnit(), 1));
root.setExpand(true);
root.setName("root");
return root;
}
private void projectClosed() {
myDependencyGraph.clear();
myTaskMap.clear();
myMaxID.set(0);
myDependencyCollection.clear();
// createRootTask();
fireTaskModelReset();
}
private void projectOpened() {
processCriticalPath(getRootTask());
myAlgorithmCollection.getRecalculateTaskCompletionPercentageAlgorithm().run(getRootTask());
}
@Override
public void deleteTask(Task tasktoRemove) {
Task container = getTaskHierarchy().getContainer(tasktoRemove);
myTaskMap.removeTask(tasktoRemove);
tasktoRemove.delete();
fireTaskRemoved(container, tasktoRemove);
}
@Override
public GanttTask createTask() {
return (GanttTask) newTaskBuilder().build();
}
@Override
public GanttTask createTask(int id) {
return (GanttTask) newTaskBuilder().withId(id).build();
}
@Override
public TaskBuilder newTaskBuilder() {
return new TaskBuilder() {
@Override
public Task build() {
if (myId == null || myTaskMap.getTask(myId) != null) {
myId = getAndIncrementId();
}
TaskImpl task = myPrototype == null ?
new GanttTask("", CalendarFactory.createGanttCalendar(), 1, TaskManagerImpl.this, myId) : new GanttTask((TaskImpl)myPrototype);
String name = myName == null ? getTaskNamePrefixOption().getValue() + "_" + task.getTaskID() : myName;
task.setName(name);
if (myStartDate != null) {
GanttCalendar cal = CalendarFactory.createGanttCalendar(myStartDate);
task.setStart(cal);
}
TimeDuration duration;
if (myDuration != null) {
duration = myDuration;
} else if (myPrototype != null) {
duration = myPrototype.getDuration();
} else {
duration = (myEndDate == null)
? createLength(getTimeUnitStack().getDefaultTimeUnit(), 1.0f)
: createLength(getTimeUnitStack().getDefaultTimeUnit(), myStartDate, myEndDate);
}
task.setDuration(duration);
if (myColor != null) {
task.setColor(myColor);
}
if (myPriority != null) {
task.setPriority(myPriority);
}
if (isExpanded != null) {
task.setExpand(isExpanded);
}
if (myNotes != null) {
task.setNotes(myNotes);
}
if (myWebLink != null) {
task.setWebLink(myWebLink);
}
if (myCompletion != null) {
task.setCompletionPercentage(myCompletion);
}
if (myCost != null) {
task.getCost().setCalculated(false);
task.getCost().setValue(myCost);
}
registerTask(task);
if (myPrevSibling != null && myPrevSibling != getRootTask()) {
int position = getTaskHierarchy().getTaskIndex(myPrevSibling) + 1;
Task parentTask = getTaskHierarchy().getContainer(myPrevSibling);
getTaskHierarchy().move(task, parentTask, position);
} else {
Task parentTask = myParent == null ? getRootTask() : myParent;
getTaskHierarchy().move(task, parentTask);
}
if (isLegacyMilestone) {
task.setMilestone(isLegacyMilestone);
}
fireTaskAdded(task);
return task;
}
};
}
protected TimeUnitStack getTimeUnitStack() {
return getConfig().getTimeUnitStack();
}
int getAndIncrementId() {
return myMaxID.getAndIncrement();
}
@Override
public void registerTask(Task task) {
int taskID = task.getTaskID();
assert myTaskMap.getTask(taskID) == null : "There is a task that already has the ID " + taskID;
myTaskMap.addTask(task);
myMaxID.set(Math.max(taskID + 1, myMaxID.get()));
myDependencyGraph.addTask(task);
}
boolean isRegistered(TaskImpl task) {
return myTaskMap.getTask(task.getTaskID()) != null;
}
@Override
public int getTaskCount() {
return myTaskMap.size();
}
private static Iterable<BarChartActivity<?>> tasksToActivities(Task[] tasks) {
return Iterables.transform(Arrays.asList(tasks), new Function<Task, BarChartActivity<?>>() {
@Override
public BarChartActivity<?> apply(final Task task) {
return new BarChartActivity<Task>() {
@Override
public Date getStart() {
return task.getStart().getTime();
}
@Override
public Date getEnd() {
return task.getEnd().getTime();
}
@Override
public TimeDuration getDuration() {
return task.getDuration();
}
@Override
public Task getOwner() {
return task;
}
};
}
});
}
@Override
public TimeDuration getProjectLength() {
if (myTaskMap.isEmpty()) {
return createLength(getConfig().getTimeUnitStack().getDefaultTimeUnit(), 0);
}
Result result = getAlgorithmCollection().getProjectBoundsAlgorithm().getBounds(tasksToActivities(myTaskMap.getTasks()));
return createLength(getConfig().getTimeUnitStack().getDefaultTimeUnit(), result.lowerBound, result.upperBound);
}
@Override
public Date getProjectStart() {
if (myTaskMap.isEmpty()) {
return myRoot.getStart().getTime();
}
Result result = getAlgorithmCollection().getProjectBoundsAlgorithm().getBounds(tasksToActivities(myTaskMap.getTasks()));
return result.lowerBound;
}
@Override
public Date getProjectEnd() {
if (myTaskMap.isEmpty()) {
return myRoot.getStart().getTime();
}
Result result = getAlgorithmCollection().getProjectBoundsAlgorithm().getBounds(tasksToActivities(myTaskMap.getTasks()));
return result.upperBound;
}
@Override
public int getProjectCompletion() {
return myRoot.getCompletionPercentage();
}
@Override
public String encode(TimeDuration taskLength) {
StringBuffer result = new StringBuffer(String.valueOf(taskLength.getLength()));
result.append(myConfig.getTimeUnitStack().encode(taskLength.getTimeUnit()));
return result.toString();
}
@Override
public TimeDuration createLength(String lengthAsString) throws DurationParsingException {
int state = 0;
StringBuffer valueBuffer = new StringBuffer();
Integer currentValue = null;
TimeDuration currentLength = null;
lengthAsString += " ";
for (int i = 0; i < lengthAsString.length(); i++) {
char nextChar = lengthAsString.charAt(i);
if (Character.isDigit(nextChar)) {
switch (state) {
case 0:
if (currentValue != null) {
throw new DurationParsingException();
}
state = 1;
valueBuffer.setLength(0);
case 1:
valueBuffer.append(nextChar);
break;
case 2:
TimeUnit timeUnit = findTimeUnit(valueBuffer.toString());
if (timeUnit == null) {
throw new DurationParsingException(valueBuffer.toString());
}
assert currentValue != null;
TimeDuration localResult = createLength(timeUnit, currentValue.floatValue());
if (currentLength == null) {
currentLength = localResult;
} else {
if (currentLength.getTimeUnit().isConstructedFrom(timeUnit)) {
float recalculatedLength = currentLength.getLength(timeUnit);
currentLength = createLength(timeUnit, localResult.getValue() + recalculatedLength);
} else {
throw new DurationParsingException();
}
}
state = 1;
currentValue = null;
valueBuffer.setLength(0);
valueBuffer.append(nextChar);
break;
}
} else if (Character.isWhitespace(nextChar)) {
switch (state) {
case 0:
break;
case 1:
currentValue = Integer.valueOf(valueBuffer.toString());
state = 0;
break;
case 2:
TimeUnit timeUnit = findTimeUnit(valueBuffer.toString());
if (timeUnit == null) {
throw new DurationParsingException(valueBuffer.toString());
}
assert currentValue != null;
TimeDuration localResult = createLength(timeUnit, currentValue.floatValue());
if (currentLength == null) {
currentLength = localResult;
} else {
if (currentLength.getTimeUnit().isConstructedFrom(timeUnit)) {
float recalculatedLength = currentLength.getLength(timeUnit);
currentLength = createLength(timeUnit, localResult.getValue() + recalculatedLength);
} else {
throw new DurationParsingException();
}
}
state = 0;
currentValue = null;
break;
}
} else {
switch (state) {
case 1:
currentValue = Integer.valueOf(valueBuffer.toString());
case 0:
if (currentValue == null) {
throw new DurationParsingException("Failed to parse value=" + lengthAsString);
}
state = 2;
valueBuffer.setLength(0);
case 2:
valueBuffer.append(nextChar);
break;
}
}
}
if (currentValue != null) {
currentValue = Integer.valueOf(valueBuffer.toString());
TimeUnit dayUnit = findTimeUnit("d");
currentLength = createLength(dayUnit, currentValue.floatValue());
}
return currentLength;
}
private TimeUnit findTimeUnit(String code) {
return myConfig.getTimeUnitStack().findTimeUnit(code);
}
@Override
public TimeDuration createLength(TimeUnit unit, float length) {
return new TimeDurationImpl(unit, length);
}
@Override
public TimeDuration createLength(long count) {
return new TimeDurationImpl(getConfig().getTimeUnitStack().getDefaultTimeUnit(), count);
}
@Override
public TimeDuration createLength(TimeUnit timeUnit, Date startDate, Date endDate) {
return getConfig().getTimeUnitStack().createDuration(timeUnit, startDate, endDate);
}
@Override
public Date shift(Date original, TimeDuration duration) {
GPCalendarCalc calendar = RESTLESS_CALENDAR;
return calendar.shiftDate(original, duration);
}
@Override
public TaskDependencyCollection getDependencyCollection() {
return myDependencyCollection;
}
@Override
public AlgorithmCollection getAlgorithmCollection() {
return myAlgorithmCollection;
}
public TaskHierarchyManagerImpl getHierarchyManager() {
return myHierarchyManager;
}
@Override
public TaskDependencyConstraint createConstraint(final TaskDependencyConstraint.Type type) {
TaskDependencyConstraint result;
switch (type) {
case finishstart:
result = new FinishStartConstraintImpl();
break;
case finishfinish:
result = new FinishFinishConstraintImpl();
break;
case startfinish:
result = new StartFinishConstraintImpl();
break;
case startstart:
result = new StartStartConstraintImpl();
break;
default:
throw new IllegalArgumentException("Unknown constraint type=" + type);
}
return result;
}
@Override
public void addTaskListener(TaskListener listener) {
myListeners.add(listener);
}
@Override
public GPCalendarCalc getCalendar() {
return getConfig().getCalendar();
}
public ProjectEventListener getProjectListener() {
return new ProjectEventListener.Stub() {
@Override
public void projectClosed() {
TaskManagerImpl.this.projectClosed();
}
@Override
public void projectOpened() {
TaskManagerImpl.this.projectOpened();
}
};
}
public GPCalendarListener getCalendarListener() {
return new GPCalendarListener() {
@Override
public void onCalendarChange() {
for (Task t : getTasks()) {
t.setEnd(null);
}
myScheduler.run();
}
};
}
public void fireTaskProgressChanged(Task changedTask) {
if (areEventsEnabled) {
TaskPropertyEvent e = new TaskPropertyEvent(changedTask);
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.taskProgressChanged(e);
}
}
}
void fireTaskScheduleChanged(Task changedTask, GanttCalendar oldStartDate, GanttCalendar oldFinishDate) {
myScheduler.run();
if (areEventsEnabled) {
TaskScheduleEvent e = new TaskScheduleEvent(changedTask, oldStartDate, oldFinishDate, changedTask.getStart(),
changedTask.getEnd());
// List copy = new ArrayList(myListeners);
// myListeners.clear();
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.taskScheduleChanged(e);
}
}
}
private void fireDependencyAdded(TaskDependency newDependency) {
myDependencyGraph.addDependency(newDependency);
if (areEventsEnabled) {
TaskDependencyEvent e = new TaskDependencyEvent(getDependencyCollection(), newDependency);
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.dependencyAdded(e);
}
}
}
private void fireDependencyRemoved(TaskDependency dep) {
myDependencyGraph.removeDependency(dep);
TaskDependencyEvent e = new TaskDependencyEvent(getDependencyCollection(), dep);
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.dependencyRemoved(e);
}
}
private void fireDependencyChanged(TaskDependency dep) {
TaskDependencyEvent e = new TaskDependencyEvent(getDependencyCollection(), dep);
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.dependencyChanged(e);
}
}
private void fireTaskAdded(Task task) {
if (areEventsEnabled) {
TaskHierarchyEvent e = new TaskHierarchyEvent(this, task, null, getTaskHierarchy().getContainer(task));
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.taskAdded(e);
}
}
}
private void fireTaskRemoved(Task container, Task task) {
myDependencyGraph.removeTask(task);
if (areEventsEnabled) {
TaskHierarchyEvent e = new TaskHierarchyEvent(this, task, container, null);
for (TaskListener l : myListeners) {
l.taskRemoved(e);
}
}
}
void fireTaskPropertiesChanged(Task task) {
if (areEventsEnabled) {
TaskPropertyEvent e = new TaskPropertyEvent(task);
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.taskPropertiesChanged(e);
}
}
}
private void fireTaskModelReset() {
if (areEventsEnabled) {
for (int i = 0; i < myListeners.size(); i++) {
TaskListener next = myListeners.get(i);
next.taskModelReset();
}
}
}
public TaskManagerConfig getConfig() {
return myConfig;
}
private final class FacadeImpl implements TaskContainmentHierarchyFacade {
// private final Task myRoot;
private List<Task> myPathBuffer = new ArrayList<Task>();
// public FacadeImpl(Task root) {
// myRoot = root;
// }
@Override
public Task[] getNestedTasks(Task container) {
return container.getNestedTasks();
}
@Override
public Task[] getDeepNestedTasks(Task container) {
ArrayList<Task> result = new ArrayList<Task>();
addDeepNestedTasks(container, result);
return result.toArray(new Task[result.size()]);
}
private void addDeepNestedTasks(Task container, ArrayList<Task> result) {
Task[] nested = container.getNestedTasks();
result.addAll(Arrays.asList(nested));
for (int i = 0; i < nested.length; i++) {
addDeepNestedTasks(nested[i], result);
}
}
@Override
public boolean hasNestedTasks(Task container) {
return container.getNestedTasks().length > 0;
}
@Override
public Task getRootTask() {
return TaskManagerImpl.this.getRootTask();
}
@Override
public Task getContainer(Task nestedTask) {
return nestedTask.getSupertask();
}
@Override
public void sort(Comparator<Task> comparator) {
throw new UnsupportedOperationException("Sort is not available int this implementation. It is stateless!");
}
@Override
public Task getPreviousSibling(Task nestedTask) {
int pos = getTaskIndex(nestedTask);
return pos == 0 ? null : nestedTask.getSupertask().getNestedTasks()[pos - 1];
}
@Override
public Task getNextSibling(Task nestedTask) {
throw new UnsupportedOperationException();
}
@Override
public int getTaskIndex(Task nestedTask) {
Task container = nestedTask.getSupertask();
if (container == null) {
return 0;
}
return Arrays.asList(container.getNestedTasks()).indexOf(nestedTask);
}
@Override
public boolean areUnrelated(Task first, Task second) {
if (first.equals(second)) {
return false;
}
myPathBuffer.clear();
for (Task container = getContainer(first); container != null; container = getContainer(container)) {
myPathBuffer.add(container);
}
if (myPathBuffer.contains(second)) {
return false;
}
myPathBuffer.clear();
for (Task container = getContainer(second); container != null; container = getContainer(container)) {
myPathBuffer.add(container);
}
if (myPathBuffer.contains(first)) {
return false;
}
return true;
}
@Override
public void move(Task whatMove, Task whereMove) {
whatMove.move(whereMove);
}
@Override
public void move(Task whatMove, Task whereMove, int index) {
whatMove.move(whereMove);
}
@Override
public int getDepth(Task task) {
int depth = 0;
while (task != myRoot) {
task = task.getSupertask();
depth++;
}
return depth;
}
@Override
public int compareDocumentOrder(Task task1, Task task2) {
if (task1 == task2) {
return 0;
}
List<Task> buffer1 = new ArrayList<Task>();
for (Task container = task1; container != null; container = getContainer(container)) {
buffer1.add(0, container);
}
List<Task> buffer2 = new ArrayList<Task>();
for (Task container = task2; container != null; container = getContainer(container)) {
buffer2.add(0, container);
}
if (buffer1.get(0) != getRootTask() && buffer2.get(0) == getRootTask()) {
return -1;
}
if (buffer1.get(0) == getRootTask() && buffer2.get(0) != getRootTask()) {
return 1;
}
int i = 0;
Task commonRoot = null;
while (true) {
if (i == buffer1.size()) {
return -1;
}
if (i == buffer2.size()) {
return 1;
}
Task root1 = buffer1.get(i);
Task root2 = buffer2.get(i);
if (root1 != root2) {
assert commonRoot != null : "Failure comparing task=" + task1 + " and task=" + task2 + "\n. Path1=" + buffer1
+ "\nPath2=" + buffer2;
Task[] nestedTasks = commonRoot.getNestedTasks();
for (int j = 0; j < nestedTasks.length; j++) {
if (nestedTasks[j] == root1) {
return -1;
}
if (nestedTasks[j] == root2) {
return 1;
}
}
throw new IllegalStateException("We should not be here");
}
i++;
commonRoot = root1;
}
}
@Override
public boolean contains(Task task) {
throw new UnsupportedOperationException();
}
@Override
public List<Task> getTasksInDocumentOrder() {
List<Task> result = Lists.newArrayList();
Deque<Task> deque = new LinkedList<Task>();
deque.addFirst(getRootTask());
while (!deque.isEmpty()) {
Task head = deque.poll();
result.addAll(0, Arrays.asList(head.getNestedTasks()));
}
return result;
}
@Override
public void breadthFirstSearch(Task root, Predicate<Pair<Task, Task>> predicate) {
Preconditions.checkNotNull(root);
Queue<Task> queue = Queues.newArrayDeque();
if (predicate.apply(Pair.create((Task) null, root))) {
queue.add(root);
}
while (!queue.isEmpty()) {
Task head = queue.poll();
for (Task child : head.getNestedTasks()) {
if (predicate.apply(Pair.create(head, child))) {
queue.add(child);
}
}
}
}
@Override
public List<Task> breadthFirstSearch(Task root, final boolean includeRoot) {
final Task _root = (root == null) ? getRootTask() : root;
final List<Task> result = Lists.newArrayList();
breadthFirstSearch(_root, new Predicate<Pair<Task,Task>>() {
public boolean apply(Pair<Task, Task> parent_child) {
if (includeRoot || parent_child.first() != null) {
result.add(parent_child.second());
}
return true;
}
});
return result;
}
@Override
public List<Integer> getOutlinePath(Task task) {
throw new UnsupportedOperationException();
}
}
private class FacadeFactoryImpl implements TaskContainmentHierarchyFacade.Factory {
// private final Task myRoot;
//
// FacadeFactoryImpl(Task root) {
// myRoot = root;
// }
@Override
public TaskContainmentHierarchyFacade createFacade() {
return new FacadeImpl();
}
}
@Override
public TaskContainmentHierarchyFacade getTaskHierarchy() {
// if (myTaskContainment==null) {
return myFacadeFactory.createFacade();
// }
// return myTaskContainment;
}
@Override
public TaskManager emptyClone() {
TaskManagerImpl result = new TaskManagerImpl(null, myConfig);
result.myDependencyHardnessOption.setValue(this.myDependencyHardnessOption.getValue());
return result;
}
@Override
public Map<Task, Task> importData(TaskManager taskManager,
Map<CustomPropertyDefinition, CustomPropertyDefinition> customPropertyMapping) {
Task importRoot = taskManager.getRootTask();
Map<Task, Task> original2imported = new LinkedHashMap<Task, Task>();
importData(importRoot, getRootTask(), customPropertyMapping, original2imported);
TaskDependency[] deps = taskManager.getDependencyCollection().getDependencies();
for (int i = 0; i < deps.length; i++) {
Task nextDependant = deps[i].getDependant();
Task nextDependee = deps[i].getDependee();
Task importedDependant = original2imported.get(nextDependant);
Task importedDependee = original2imported.get(nextDependee);
try {
TaskDependency dependency = getDependencyCollection().createDependency(importedDependant, importedDependee,
new FinishStartConstraintImpl());
dependency.setConstraint(deps[i].getConstraint());
dependency.setDifference(deps[i].getDifference());
dependency.setHardness(deps[i].getHardness());
} catch (TaskDependencyException e) {
if (!GPLogger.log(e)) {
e.printStackTrace(System.err);
}
}
}
return original2imported;
}
private void importData(Task importRoot, Task root,
Map<CustomPropertyDefinition, CustomPropertyDefinition> customPropertyMapping, Map<Task, Task> original2imported) {
Task[] nested = importRoot.getManager().getTaskHierarchy().getNestedTasks(importRoot);
for (int i = 0; i < nested.length; i++) {
TaskManager.TaskBuilder builder = newTaskBuilder();
GanttTask that = (GanttTask) nested[i];
if (getTask(that.getTaskID()) == null) {
builder = builder.withId(that.getTaskID());
}
Task nextImported = builder.withName(that.getName()).withStartDate(that.getStart().getTime())
.withDuration(that.getDuration())
.withColor(that.getColor()).withNotes(that.getNotes()).withWebLink(that.getWebLink()).withParent(root).build();
nextImported.setShape(nested[i].getShape());
nextImported.setCompletionPercentage(nested[i].getCompletionPercentage());
nextImported.setTaskInfo(nested[i].getTaskInfo());
nextImported.setExpand(nested[i].getExpand());
nextImported.setMilestone(nested[i].isMilestone());
nextImported.getCost().setValue(that.getCost());
if (nested[i].getThird() != null) {
nextImported.setThirdDate(nested[i].getThird().clone());
nextImported.setThirdDateConstraint(nested[i].getThirdDateConstraint());
}
CustomColumnsValues customValues = nested[i].getCustomValues();
for (CustomPropertyDefinition thatDef : importRoot.getManager().getCustomPropertyManager().getDefinitions()) {
CustomPropertyDefinition thisDef = customPropertyMapping.get(thatDef);
Object value = customValues.getValue(thatDef);
if (value != null) {
try {
nextImported.getCustomValues().setValue(thisDef, value);
} catch (CustomColumnsException e) {
if (!GPLogger.log(e)) {
e.printStackTrace(System.err);
}
}
}
}
original2imported.put(nested[i], nextImported);
importData(nested[i], nextImported, customPropertyMapping, original2imported);
}
}
public Date findClosestWorkingTime(Date time) {
return getCalendar().findClosestWorkingTime(time);
}
@Override
public void processCriticalPath(Task root) {
try {
myAlgorithmCollection.getRecalculateTaskScheduleAlgorithm().run();
} catch (TaskDependencyException e) {
if (!GPLogger.log(e)) {
e.printStackTrace(System.err);
}
}
Task[] tasks = myAlgorithmCollection.getCriticalPathAlgorithm().getCriticalTasks();
resetCriticalPath();
for (int i = 0; i < tasks.length; i++) {
tasks[i].setCritical(true);
}
}
private void resetCriticalPath() {
Task[] allTasks = getTasks();
for (int i = 0; i < allTasks.length; i++) {
allTasks[i].setCritical(false);
}
}
@Override
public void importAssignments(TaskManager importedTaskManager, HumanResourceManager hrManager,
Map<Task, Task> original2importedTask, Map<HumanResource, HumanResource> original2importedResource) {
Task[] tasks = importedTaskManager.getTasks();
for (int i = 0; i < tasks.length; i++) {
ResourceAssignment[] assignments = tasks[i].getAssignments();
for (int j = 0; j < assignments.length; j++) {
Task task = getTask(original2importedTask.get(tasks[i]).getTaskID());
ResourceAssignment assignment = task.getAssignmentCollection().addAssignment(
original2importedResource.get(assignments[j].getResource()));
assignment.setLoad(assignments[j].getLoad());
assignment.setCoordinator(assignments[j].isCoordinator());
}
}
}
void onTaskMoved(TaskImpl task) {
if (!isRegistered(task)) {
registerTask(task);
}
myDependencyGraph.move(task, getTaskHierarchy().getContainer(task));
myTaskMap.setDirty();
}
public void setEventsEnabled(boolean enabled) {
areEventsEnabled = enabled;
}
boolean areEventsEnabled() {
return areEventsEnabled;
}
@Override
public CustomPropertyManager getCustomPropertyManager() {
return myCustomColumnsManager;
}
public URL getProjectDocument() {
return myConfig.getProjectDocumentURL();
}
private static class TaskNamePrefixOption extends DefaultStringOption implements GP1XOptionConverter {
public TaskNamePrefixOption() {
super("taskNamePrefix");
resetValue(GanttLanguage.getInstance().getText("defaultTaskPrefix"), true);
}
@Override
public String getTagName() {
return "task-name";
}
@Override
public String getAttributeName() {
return "prefix";
}
@Override
public void loadValue(String legacyValue) {
resetValue(legacyValue, true);
}
}
@Override
public StringOption getTaskNamePrefixOption() {
return myTaskNamePrefixOption;
}
@Override
public StringOption getTaskCopyNamePrefixOption() {
return myTaskCopyNamePrefixOption;
}
@Override
public EnumerationOption getDependencyHardnessOption() {
return myDependencyHardnessOption;
}
@Override
public void setZeroMilestones(Boolean b) {
isZeroMilestones = b;
if (Boolean.TRUE == isZeroMilestones) {
List<Task> milestones = Lists.newArrayList();
for (Task t : getTasks()) {
if (t.isMilestone()) {
t.setMilestone(true);
milestones.add(t);
}
}
getAlgorithmCollection().getAdjustTaskBoundsAlgorithm().run(milestones);
try {
getAlgorithmCollection().getRecalculateTaskScheduleAlgorithm().run(milestones);
} catch (TaskDependencyException e) {
GPLogger.log(e);
}
}
}
@Override
public Boolean isZeroMilestones() {
return isZeroMilestones;
}
@Override
public DependencyGraph getDependencyGraph() {
return myDependencyGraph;
}
}