From 981df70033c2ae3441692d21e756b8f2f4c98531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Mon, 27 Apr 2009 15:57:33 +0200 Subject: [PATCH] ItEr05S03ArquitecturaClientesItEr04S03: Start-End dependencies added. It uses jgrapht library, so dependency was added. --- navalplanner-gantt-zk/pom.xml | 5 + .../java/org/zkoss/ganttz/Dependency.java | 24 ++-- .../java/org/zkoss/ganttz/DependencyList.java | 18 ++- .../java/org/zkoss/ganttz/ListDetails.java | 2 +- .../main/java/org/zkoss/ganttz/Planner.java | 18 ++- .../src/main/java/org/zkoss/ganttz/Task.java | 5 +- .../java/org/zkoss/ganttz/TaskDetail.java | 1 + .../zkoss/ganttz/TaskEditFormComposer.java | 1 + .../org/zkoss/ganttz/util/DependencyBean.java | 113 ++++++++++++++++++ .../zkoss/ganttz/util/DependencyRegistry.java | 107 +++++++++++++++++ .../org/zkoss/ganttz/util/DependencyType.java | 51 ++++++++ .../org/zkoss/ganttz/{ => util}/TaskBean.java | 10 +- 12 files changed, 340 insertions(+), 15 deletions(-) create mode 100644 navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyBean.java create mode 100644 navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyRegistry.java create mode 100644 navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyType.java rename navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/{ => util}/TaskBean.java (91%) diff --git a/navalplanner-gantt-zk/pom.xml b/navalplanner-gantt-zk/pom.xml index dd6820a9a..e2a167f08 100644 --- a/navalplanner-gantt-zk/pom.xml +++ b/navalplanner-gantt-zk/pom.xml @@ -31,6 +31,11 @@ commons-logging commons-logging + + jgrapht + jgrapht + 0.7.3 + diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Dependency.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Dependency.java index 881123873..5a867f6e7 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Dependency.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Dependency.java @@ -8,6 +8,8 @@ package org.zkoss.ganttz; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import org.zkoss.ganttz.util.DependencyBean; +import org.zkoss.ganttz.util.DependencyType; import org.zkoss.zk.au.out.AuInvoke; import org.zkoss.zk.ui.ext.AfterCompose; import org.zkoss.zul.impl.XulElement; @@ -21,14 +23,6 @@ public class Dependency extends XulElement implements AfterCompose { private Task source; - public Task getSource() { - return source; - } - - public Task getDestination() { - return destination; - } - private Task destination; public Dependency() { @@ -96,4 +90,18 @@ public class Dependency extends XulElement implements AfterCompose { public boolean contains(Task task) { return getSource().equals(task) || getDestination().equals(task); } + + public Task getSource() { + return source; + } + + public Task getDestination() { + return destination; + } + + public DependencyBean getDependencyBean() { + return new DependencyBean(source.getTaskBean(), destination + .getTaskBean(), DependencyType.END_START); + } + } diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/DependencyList.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/DependencyList.java index 6e76c366f..34aa0aa44 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/DependencyList.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/DependencyList.java @@ -42,6 +42,7 @@ public class DependencyList extends XulElement implements AfterCompose { void addDependency(Dependency dependency) { appendChild(dependency); addContextMenu(dependency); + publishDependency(dependency); } private void addContextMenu(Dependency dependency) { @@ -83,9 +84,24 @@ public class DependencyList extends XulElement implements AfterCompose { taskRemovedListener); } addContextMenu(); + publishDependencies(); } + private void publishDependencies() { + for (Dependency dependency : getDependencies()) { + publishDependency(dependency); + } + } + + private void publishDependency(Dependency dependency) { + getPlanner().publishDependency(dependency); + } + + private Planner getPlanner() { + return getGanttPanel().getPlanner(); + } + private void addContextMenu() { for (Dependency dependency : getDependencies()) { addContextMenu(dependency); @@ -94,8 +110,6 @@ public class DependencyList extends XulElement implements AfterCompose { private Menupopup contextMenu; - private Dependency dependencyForContextMenu = null; - private Menupopup getContextMenu() { if (contextMenu == null) { contextMenu = MenuBuilder.on(getPage(), getDependencies()).item( diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/ListDetails.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/ListDetails.java index 3e1d05d44..41048f601 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/ListDetails.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/ListDetails.java @@ -33,7 +33,7 @@ public class ListDetails extends HtmlMacroComponent { taskDetail.setParent(insertionPoint); taskDetail.afterCompose(); Task task = new Task(); - getPlanner().addTask(task); + getPlanner().publishTask(task); task.setColor("yellow"); task.setId(newId); } diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Planner.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Planner.java index 4eaa4dc68..89807f93c 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Planner.java @@ -5,6 +5,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.zkoss.ganttz.util.DependencyRegistry; +import org.zkoss.ganttz.util.TaskBean; import org.zkoss.zk.ui.ext.AfterCompose; import org.zkoss.zul.impl.XulElement; @@ -14,6 +16,8 @@ public class Planner extends XulElement implements AfterCompose { private Map tasksById = new HashMap(); + private DependencyRegistry dependencyRegistry = new DependencyRegistry(); + public Planner() { } @@ -42,6 +46,7 @@ public class Planner extends XulElement implements AfterCompose { throw new IllegalArgumentException("task with id " + taskId + " is already in " + tasksById); tasksById.put(taskId, task); + dependencyRegistry.add(task); } TaskBean retrieve(String taskId) { @@ -78,10 +83,21 @@ public class Planner extends XulElement implements AfterCompose { } }; taskList.addDependencyListener(dependencyAddedListener); + taskList.addTaskRemovedListener(new TaskRemovedListener() { + @Override + public void taskRemoved(Task taskRemoved) { + dependencyRegistry.remove(taskRemoved.getTaskBean()); + } + }); } - public void addTask(Task task) { + public void publishTask(Task task) { getTaskList().addTask(task); + dependencyRegistry.add(task.getTaskBean()); + } + + public void publishDependency(Dependency dependency) { + dependencyRegistry.add(dependency); } } diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Task.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Task.java index 87f0c4525..04643e73d 100755 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Task.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/Task.java @@ -15,6 +15,7 @@ import java.util.ListIterator; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.zkoss.ganttz.util.TaskBean; import org.zkoss.lang.Objects; import org.zkoss.xml.HTMLs; import org.zkoss.zk.au.AuRequest; @@ -212,9 +213,9 @@ public class Task extends Div { this.taskBean.setLengthMilliseconds(getMapper().toMilliseconds(pixels)); } - void doAddDependency(String dependencyId) { + void doAddDependency(String destinyTaskId) { Dependency dependency = new Dependency(this, - ((Task) getFellow(dependencyId))); + ((Task) getFellow(destinyTaskId))); fireDependenceAdded(dependency); } diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskDetail.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskDetail.java index 413801f14..6913ebe60 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskDetail.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskDetail.java @@ -14,6 +14,7 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.zkoss.ganttz.util.TaskBean; import org.zkoss.zk.ui.AbstractComponent; import org.zkoss.zk.ui.HtmlMacroComponent; import org.zkoss.zk.ui.ext.AfterCompose; diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskEditFormComposer.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskEditFormComposer.java index 2c6c93f21..84df5ec13 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskEditFormComposer.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskEditFormComposer.java @@ -3,6 +3,7 @@ package org.zkoss.ganttz; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import org.zkoss.ganttz.util.TaskBean; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.util.GenericForwardComposer; diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyBean.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyBean.java new file mode 100644 index 000000000..7b80951a5 --- /dev/null +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyBean.java @@ -0,0 +1,113 @@ +package org.zkoss.ganttz.util; + +import java.util.Collection; +import java.util.Date; + +/** + * This class represents a dependency. Contains the source and the destination. + * It also specifies the type of the relationship.
+ * Created at Apr 24, 2009 + * + * @author Óscar González Fernández + * + */ +public class DependencyBean { + + private enum Calculation { + START, END; + } + + public static Date calculateStart(TaskBean origin, Date current, + Collection dependencies) { + return apply(Calculation.START, origin, current, dependencies); + } + + public static Date calculateEnd(TaskBean origin, Date current, + Collection depencencies) { + return apply(Calculation.END, origin, current, depencencies); + } + + private static Date apply(Calculation calculation, TaskBean origin, + Date current, Collection dependencies) { + for (DependencyBean dependency : dependencies) { + switch (calculation) { + case START: + current = dependency.getType().calculateStartDestinyTask( + dependency.getSource(), current); + break; + case END: + current = dependency.getType().calculateEndDestinyTask( + dependency.getSource(), current); + break; + default: + throw new RuntimeException("unexpected calculation " + + calculation); + } + } + return current; + } + + private final TaskBean source; + + private final TaskBean destination; + + private final DependencyType type; + + public DependencyBean(TaskBean source, TaskBean destination, + DependencyType type) { + if (source == null) + throw new IllegalArgumentException("source cannot be null"); + if (destination == null) + throw new IllegalArgumentException("destination cannot be null"); + if (type == null) + throw new IllegalArgumentException("type cannot be null"); + this.source = source; + this.destination = destination; + this.type = type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((destination == null) ? 0 : destination.hashCode()); + result = prime * result + ((source == null) ? 0 : source.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DependencyBean other = (DependencyBean) obj; + if (destination == null) { + if (other.destination != null) + return false; + } else if (!destination.equals(other.destination)) + return false; + if (source == null) { + if (other.source != null) + return false; + } else if (!source.equals(other.source)) + return false; + return true; + } + + public TaskBean getSource() { + return source; + } + + public TaskBean getDestination() { + return destination; + } + + public DependencyType getType() { + return type; + } + +} diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyRegistry.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyRegistry.java new file mode 100644 index 000000000..7aadad658 --- /dev/null +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyRegistry.java @@ -0,0 +1,107 @@ +package org.zkoss.ganttz.util; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.DirectedGraph; +import org.jgrapht.graph.SimpleDirectedGraph; +import org.zkoss.ganttz.Dependency; + +/** + * This class contains a graph with the {@link TaskBean tasks} as vertexes and + * the {@link DependencyBean dependency} as arcs. It enforces the rules embodied + * in the dependencies and in the duration of the tasks using listeners.
+ * Created at Apr 24, 2009 + * + * @author Óscar González Fernández + * + */ +public class DependencyRegistry { + + private final DirectedGraph graph = new SimpleDirectedGraph( + DependencyBean.class); + + private Map rulesEnforcersByTask = new HashMap(); + + private List getOutgoing(TaskBean task) { + ArrayList result = new ArrayList(); + for (DependencyBean dependencyBean : graph.outgoingEdgesOf(task)) { + result.add(rulesEnforcersByTask + .get(dependencyBean.getDestination())); + } + return result; + } + + private class RulesEnforcer { + private final TaskBean task; + + private RulesEnforcer(TaskBean task) { + if (task == null) + throw new IllegalArgumentException("task cannot be null"); + this.task = task; + this.task.addPropertyChangeListener(new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + RulesEnforcer.this.update(); + updateOutgoing(RulesEnforcer.this.task); + } + }); + } + + void update() { + Set incoming = graph.incomingEdgesOf(task); + Date beginDate = task.getBeginDate(); + Date newStart = DependencyBean.calculateStart(task, beginDate, + incoming); + if (!beginDate.equals(newStart)) + task.setBeginDate(newStart); + Date endDate = task.getEndDate(); + Date newEnd = DependencyBean.calculateEnd(task, endDate, incoming); + if (!endDate.equals(newEnd)) { + task.setEndDate(newEnd); + } + } + } + + public void add(TaskBean task) { + graph.addVertex(task); + rulesEnforcersByTask.put(task, new RulesEnforcer(task)); + } + + public void remove(TaskBean task) { + graph.removeVertex(task); + rulesEnforcersByTask.remove(task); + updateOutgoing(task); + } + + private void updateOutgoing(TaskBean task) { + for (RulesEnforcer rulesEnforcer : getOutgoing(task)) { + rulesEnforcer.update(); + } + } + + public void remove(Dependency dependency) { + graph.removeEdge(dependency.getDependencyBean()); + TaskBean destination = dependency.getDependencyBean().getDestination(); + rulesEnforcersByTask.get(destination).update(); + } + + public void add(Dependency dependency) { + TaskBean destination = dependency.getDestination().getTaskBean(); + graph.addEdge(dependency.getSource().getTaskBean(), destination, + dependency.getDependencyBean()); + getEnforcer(destination).update(); + } + + private RulesEnforcer getEnforcer(TaskBean destination) { + return rulesEnforcersByTask.get(destination); + } + +} diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyType.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyType.java new file mode 100644 index 000000000..15b4644af --- /dev/null +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/DependencyType.java @@ -0,0 +1,51 @@ +package org.zkoss.ganttz.util; + +import java.util.Date; + +/** + * This enum tells the type of a depepdency. Each instance contanins the correct + * behaviour for that type of dependency .
+ * Created at Apr 24, 2009 + * + * @author Óscar González Fernández + * + */ +public enum DependencyType { + + VOID { + @Override + public Date calculateEndDestinyTask(TaskBean originalTask, Date current) { + return current; + } + + @Override + public Date calculateStartDestinyTask(TaskBean originalTask, + Date current) { + return current; + } + }, + END_START { + @Override + public Date calculateEndDestinyTask(TaskBean originalTask, Date current) { + return current; + } + + @Override + public Date calculateStartDestinyTask(TaskBean originalTask, + Date current) { + return getBigger(originalTask.getEndDate(), current); + } + }; + + private static Date getBigger(Date date1, Date date2) { + if (date1.before(date2)) + return date2; + return date1; + } + + public abstract Date calculateEndDestinyTask(TaskBean originTask, + Date current); + + public abstract Date calculateStartDestinyTask(TaskBean originTask, + Date current); +} diff --git a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskBean.java b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/TaskBean.java similarity index 91% rename from navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskBean.java rename to navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/TaskBean.java index 6b42056ce..08fc065c1 100644 --- a/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/TaskBean.java +++ b/navalplanner-gantt-zk/src/main/java/org/zkoss/ganttz/util/TaskBean.java @@ -1,9 +1,17 @@ -package org.zkoss.ganttz; +package org.zkoss.ganttz.util; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Date; +/** + * This class contains the information of a task. It can be modified and + * notifies of the changes to the interested parties.
+ * Created at Apr 24, 2009 + * + * @author Óscar González Fernández + * + */ public class TaskBean { private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(