/*
GanttProject is an opensource project management tool.
Copyright (C) 2005-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;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Comparator;
import java.util.Arrays;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import org.jdesktop.swingx.treetable.TreeTableNode;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.task.TaskManagerImpl;
import net.sourceforge.ganttproject.task.TaskNode;
import net.sourceforge.ganttproject.task.dependency.TaskDependencyException;
import net.sourceforge.ganttproject.util.collect.Pair;
class TaskContainmentHierarchyFacadeImpl implements TaskContainmentHierarchyFacade {
private Map<Task, MutableTreeTableNode> myTask2treeNode = new HashMap<Task, MutableTreeTableNode>();
private Map<Task, Integer> myTask2index = new LinkedHashMap<Task, Integer>();
private Task myRootTask;
private List<Task> myPathBuffer = new ArrayList<Task>();
private GanttTree2 myTree;
public TaskContainmentHierarchyFacadeImpl(GanttTree2 tree) {
List<MutableTreeTableNode> allTasks = tree.getAllTasks();
for (int i = 0; i < allTasks.size(); i++) {
MutableTreeTableNode treeNode = allTasks.get(i);
Task task = (Task) treeNode.getUserObject();
if (treeNode == tree.getRoot()) {
myRootTask = task;
}
myTask2treeNode.put(task, treeNode);
myTask2index.put(task, new Integer(i));
}
myTree = tree;
}
@Override
public Task[] getNestedTasks(Task container) {
Task[] result = null;
MutableTreeTableNode treeNode = myTask2treeNode.get(container);
if (treeNode != null) {
ArrayList<Task> list = new ArrayList<Task>();
for (Enumeration children = treeNode.children(); children.hasMoreElements();) {
DefaultMutableTreeTableNode next = (DefaultMutableTreeTableNode) children.nextElement();
if (next instanceof TaskNode) {
list.add((Task) next.getUserObject());
}
}
result = list.toArray(new Task[0]);
}
return result == null ? new Task[0] : result;
}
@Override
public Task[] getDeepNestedTasks(Task container) {
ArrayList<Task> result = new ArrayList<Task>();
MutableTreeTableNode treeNodes = myTask2treeNode.get(container);
if (treeNodes != null) {
for (MutableTreeTableNode curNode : TreeUtil.collectSubtree(treeNodes)) {
assert curNode.getUserObject() instanceof Task;
result.add((Task) curNode.getUserObject());
}
// We remove the first task which is == container
assert result.size() > 0;
result.remove(0);
}
return result.toArray(new Task[result.size()]);
}
/**
* Purpose: Returns true if the container Task has any nested tasks. This
* should be a quicker check than using getNestedTasks().
*
* @param container
* The Task on which to check for children.
*/
@Override
public boolean hasNestedTasks(Task container) {
MutableTreeTableNode treeNode = myTask2treeNode.get(container);
if (treeNode != null) {
if (treeNode.children().hasMoreElements()) {
return true;
}
}
return false;
}
@Override
public Task getRootTask() {
return myRootTask;
}
@Override
public Task getContainer(Task nestedTask) {
MutableTreeTableNode treeNode = myTask2treeNode.get(nestedTask);
if (treeNode == null) {
return null;
}
MutableTreeTableNode containerNode = (MutableTreeTableNode) treeNode.getParent();
return containerNode == null ? null : (Task) containerNode.getUserObject();
}
@Override
public void sort(Comparator<Task> comparator) {
Task[] tasks = getDeepNestedTasks(getRootTask());
HashMap<Task, Boolean> expanded = new HashMap<>();
for (Task t : tasks) {
expanded.put(t, myTree.isExpanded(t));
}
sortHelper(getRootTask(), comparator);
for (Task t : tasks) {
myTree.setExpanded(t, expanded.get(t));
}
}
private void sortHelper(Task root, Comparator<Task> comparator) {
Task[] tasks = getNestedTasks(root);
Arrays.sort(tasks, comparator);
for (Task t : tasks) {
myTree.getModel().removeNodeFromParent(myTask2treeNode.get(t));
}
for (int i = 0; i < tasks.length; i++) {
myTree.getModel().insertNodeInto(myTask2treeNode.get(tasks[i]), myTask2treeNode.get(root), i);
sortHelper(tasks[i], comparator);
}
}
@Override
public Task getPreviousSibling(Task nestedTask) {
MutableTreeTableNode treeNode = myTask2treeNode.get(nestedTask);
assert treeNode != null : "TreeNode of " + nestedTask + " not found. Please inform GanttProject developers";
TreeTableNode siblingNode = TreeUtil.getPrevSibling(treeNode);
return siblingNode == null ? null : (Task) siblingNode.getUserObject();
}
@Override
public Task getNextSibling(Task nestedTask) {
MutableTreeTableNode treeNode = myTask2treeNode.get(nestedTask);
assert treeNode != null : "TreeNode of " + nestedTask + " not found. Please inform GanttProject developers";
TreeTableNode siblingNode = TreeUtil.getNextSibling(treeNode);
return siblingNode == null ? null : (Task) siblingNode.getUserObject();
}
@Override
public int getTaskIndex(Task nestedTask) {
MutableTreeTableNode treeNode = myTask2treeNode.get(nestedTask);
assert treeNode != null : "TreeNode of " + nestedTask + " not found. Please inform GanttProject developers";
TreeNode containerNode = treeNode.getParent();
return containerNode.getIndex(treeNode);
}
@Override
public List<Integer> getOutlinePath(Task task) {
int depth = getDepth(task);
List<Integer> result = Lists.newArrayListWithExpectedSize(depth);
TreeNode node = myTask2treeNode.get(task);
for (int i = 0; i < depth; i++) {
TreeNode containerNode = node.getParent();
result.add(i, containerNode.getIndex(node) + 1);
node = containerNode;
}
return Lists.reverse(result);
}
@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) {
MutableTreeTableNode targetNode = myTask2treeNode.get(whereMove);
assert targetNode != null : "Failed to find tree node for task=" + whereMove;
MutableTreeTableNode currentNode = myTask2treeNode.get(whatMove);
if (currentNode != null && currentNode.getParent() == targetNode) {
return;
}
move(whatMove, whereMove, targetNode.getChildCount());
}
@Override
public void move(Task whatMove, Task whereMove, int index) {
MutableTreeTableNode targetNode = myTask2treeNode.get(whereMove);
MutableTreeTableNode movedNode = myTask2treeNode.get(whatMove);
if (movedNode == null) {
movedNode = myTree.addObjectWithExpand(whatMove, targetNode);
}
TreePath movedPath = TreeUtil.createPath(movedNode);
boolean wasSelected = (myTree.getJTree().getTreeSelectionModel().isPathSelected(movedPath));
if (wasSelected) {
myTree.getJTree().getTreeSelectionModel().removeSelectionPath(movedPath);
}
myTree.getModel().removeNodeFromParent(movedNode);
myTree.getModel().insertNodeInto(movedNode, targetNode, index);
if (wasSelected) {
movedPath = TreeUtil.createPath(movedNode);
myTree.getJTree().getTreeSelectionModel().addSelectionPath(movedPath);
}
((TaskManagerImpl)getTaskManager()).getDependencyGraph().move(whatMove, whereMove == getTaskManager().getRootTask() ? null : whereMove);
getTaskManager().getAlgorithmCollection().getAdjustTaskBoundsAlgorithm().run(whatMove);
try {
getTaskManager().getAlgorithmCollection().getRecalculateTaskScheduleAlgorithm().run();
} catch (TaskDependencyException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private TaskManager getTaskManager() {
return myRootTask.getManager();
}
@Override
public int getDepth(Task task) {
MutableTreeTableNode treeNode = myTask2treeNode.get(task);
return TreeUtil.getLevel(treeNode);
}
@Override
public int compareDocumentOrder(Task task1, Task task2) {
Integer index1 = myTask2index.get(task1);
Integer index2 = myTask2index.get(task2);
return index1.intValue() - index2.intValue();
}
@Override
public boolean contains(Task task) {
return myTask2treeNode.containsKey(task);
}
private static final Function<MutableTreeTableNode, Task> ourNodeToTaskFxn = new Function<MutableTreeTableNode, Task>() {
@Override
public Task apply(MutableTreeTableNode input) {
return input == null ? null : (Task) input.getUserObject();
}
};
@Override
public List<Task> getTasksInDocumentOrder() {
MutableTreeTableNode rootNode = myTask2treeNode.get(getRootTask());
List<MutableTreeTableNode> subtree = TreeUtil.collectSubtree(rootNode);
return Lists.transform(subtree.subList(1, subtree.size()), ourNodeToTaskFxn);
}
@Override
public void breadthFirstSearch(Task root, final Predicate<Pair<Task, Task>> predicate) {
Preconditions.checkNotNull(root);
MutableTreeTableNode rootNode = myTask2treeNode.get(root);
TreeUtil.breadthFirstSearch(rootNode, new Predicate<Pair<MutableTreeTableNode,MutableTreeTableNode>>() {
@Override
public boolean apply(Pair<MutableTreeTableNode, MutableTreeTableNode> parent_child) {
Task parentTask = ourNodeToTaskFxn.apply(parent_child.first());
Task childTask = ourNodeToTaskFxn.apply(parent_child.second());
return predicate.apply(Pair.create(parentTask, childTask));
}
});
}
@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>>() {
@Override
public boolean apply(Pair<Task, Task> parent_child) {
if (includeRoot || parent_child.first() != null) {
result.add(parent_child.second());
}
return true;
}
});
return result;
}
}