/* GanttProject is an opensource project management tool. Copyright (C) 2009-2011 Dmitry Barashev, 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.algorithm; import biz.ganttproject.core.calendar.GPCalendarCalc; import net.sourceforge.ganttproject.GPLogger; import net.sourceforge.ganttproject.task.Task; import net.sourceforge.ganttproject.task.TaskManager; import net.sourceforge.ganttproject.task.dependency.TaskDependency; import net.sourceforge.ganttproject.task.dependency.TaskDependencyConstraint.Collision; import java.util.*; import java.util.logging.Logger; public class CriticalPathAlgorithmImpl implements CriticalPathAlgorithm { private static final Logger ourLogger = GPLogger.getLogger(CriticalPathAlgorithm.class); private final TaskManager myTaskManager; private final GPCalendarCalc myCalendar; public CriticalPathAlgorithmImpl(TaskManager taskManager, GPCalendarCalc calendar) { myTaskManager = taskManager; myCalendar = calendar; } static class Node { private final Task task; private final List<Task> dependees = new ArrayList<Task>(); private int numDependants; private final Date est; private final Date eft; private Date lst; private Date lft; private boolean lftFromSupertask = false; public Node(Task t, Set<Task> taskScope) { assert t != null; task = t; est = t.getStart().getTime(); eft = t.getEnd().getTime(); lst = null; lft = null; numDependants = 0; TaskDependency[] deps = t.getDependenciesAsDependee().toArray(); for (int i = 0; i < deps.length; i++) { if (taskScope.contains(deps[i].getDependant())) { numDependants++; } } collectDependees(t, taskScope); } public Node(Task t, Date est, Date eft, Date lst, Date lft, int numDependants, Set<Task> taskScope) { task = t; this.est = est; this.eft = eft; this.lst = lst; this.lft = lft; this.numDependants = numDependants; if (task != null) { collectDependees(task, taskScope); } } void collectDependees(Task task, Set<Task> taskScope) { TaskDependency[] deps = task.getDependenciesAsDependant().toArray(); for (TaskDependency dep : deps) { if (taskScope.contains(dep.getDependee())) { dependees.add(dep.getDependee()); } } } boolean isCritical() { return est.equals(lst); } @Override public String toString() { return task == null ? "[Deadline node " + eft + "]" : task.toString(); } } @Override public Task[] getCriticalTasks() { Date projectEnd = myTaskManager.getProjectEnd(); Node fakeFinalNode = new Node(null, projectEnd, projectEnd, projectEnd, projectEnd, 0, null); Task[] tasks = myTaskManager.getTasks(); if (tasks.length == 0) { return tasks; } Map<Task, Node> task_node = createTaskNodeMap(tasks, fakeFinalNode); for (Node curNode : task_node.values()) { curNode.numDependants += myTaskManager.getTaskHierarchy().getDepth(curNode.task) - 1; } assert fakeFinalNode.dependees.size() > 0; LinkedHashSet<Task> result = new LinkedHashSet<Task>(); Processor p = new Processor(task_node, fakeFinalNode); result.addAll(p.run()); return result.toArray(new Task[result.size()]); } private Map<Task, Node> createTaskNodeMap(Task[] tasks, Node deadlineNode) { Set<Task> taskScope = new HashSet<Task>(Arrays.asList(tasks)); Map<Task, Node> task_node = new HashMap<Task, Node>(); for (Task task : tasks) { Node newNode = new Node(task, taskScope); deadlineNode.dependees.add(task); newNode.numDependants++; task_node.put(task, newNode); } return task_node; } class Processor { private final Map<Task, Node> myTask_Node; private LinkedList<Node> myQueue = new LinkedList<Node>(); private final ArrayList<Task> myResult = new ArrayList<Task>(); private final Node myDeadlineNode; Processor(Map<Task, Node> task_node, Node deadlineNode) { myDeadlineNode = deadlineNode; myTask_Node = task_node; myQueue.add(myDeadlineNode); } boolean hasMoreInput() { return !myQueue.isEmpty(); } List<Task> run() { while (hasMoreInput()) { myQueue = processQueue(); } return myResult; } private LinkedList<Node> processQueue() { LinkedList<Node> newQueue = new LinkedList<Node>(); for (Iterator<Node> nodes = myQueue.iterator(); nodes.hasNext();) { Node curNode = nodes.next(); if (curNode.lft == null || curNode.lftFromSupertask) { calculateLatestDates(curNode); Task[] nestedTasks = myTaskManager.getTaskHierarchy().getNestedTasks(curNode.task); for (Task nestedTask : nestedTasks) { Node nested = myTask_Node.get(nestedTask); nested.numDependants -= (myTaskManager.getTaskHierarchy().getDepth(nested.task) - 1); assert nested.numDependants >= 0; if (nested.numDependants == 0) { newQueue.add(nested); } if (curNode.isCritical()) { nested.lft = curNode.lft; nested.lftFromSupertask = true; } } if (curNode.isCritical()) { ourLogger.fine("\n\nNode=" + curNode + " is critical\n\n"); myResult.add(curNode.task); } } else { assert curNode.task == null || curNode.lftFromSupertask; } enqueueDependees(newQueue, curNode); } return newQueue; } private void calculateLatestDates(Node curNode) { ourLogger.fine("Calculating latest dates for:" + curNode); curNode.lft = findLatestFinishTime(myTask_Node, curNode); curNode.lst = myCalendar.shiftDate(curNode.lft, myTaskManager.createLength(-curNode.task.getDuration().getLength())); ourLogger.fine("latest start date=" + curNode.lst); } private void enqueueDependees(LinkedList<Node> newQueue, Node curNode) { for (int i = 0; i < curNode.dependees.size(); i++) { Task dependeeTask = curNode.dependees.get(i); Node dependeeNode = myTask_Node.get(dependeeTask); assert dependeeNode.numDependants > 0; if (--dependeeNode.numDependants == 0) { newQueue.add(dependeeNode); } } } private Date findLatestFinishTime(Map<Task, Node> task_node, Node curNode) { Date result = curNode.lft; Node resultNode = null; TaskDependency[] deps = curNode.task.getDependenciesAsDependee().toArray(); for (TaskDependency dep : deps) { Node depNode = task_node.get(dep.getDependant()); if (depNode != null) { Date lft = findLatestFinishTime(curNode, depNode, dep); if (result == null || result.after(lft)) { result = lft; resultNode = depNode; } } } if (result == null || result.after(myDeadlineNode.lft)) { result = myDeadlineNode.lft; } ourLogger.fine("latest finish time=" + result + " (defined by:" + resultNode + ")"); return result; } Date findLatestFinishTime(Node curNode, Node depNode, TaskDependency dep) { Collision backwardCollision = dep.getConstraint().getBackwardCollision(depNode.lst); if (backwardCollision == null) { return depNode.lst; } return backwardCollision.getAcceptableStart().getTime(); } } }