Some API docs for the entering/reentering part

This commit is contained in:
Óscar González Fernández 2012-03-01 13:34:56 +01:00 committed by Jacobo Aragunde Pérez
parent ee88157afb
commit b58315b5df
2 changed files with 93 additions and 6 deletions

View file

@ -566,13 +566,44 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
public void setNewEnd(GanttDate previousEnd, GanttDate newEnd);
}
/**
* <p>
* When a {@link Task}'s dates are modified methods of this interface must
* be called. This can potentially trigger the dependencies enforcement
* algorithm.
* </p>
* <p>
* If the date modification happens outside the dependencies enforcement
* algorithm, it's always executed. Through the algorithm execution other
* tasks' dates are modified. When this happens we don't want to trigger the
* algorithm, instead we want to record that the change has happened and
* when the algorithm ends all the tasks are notified at once.
* </p>
* <p>
* For example imagine a Gantt with three tasks: T1 -> T2 -> T3. Imagine
* that T1 position is modified due to being moved by the user. In that case
* the scheduling algorithm triggers and the {@link Recalculation
* recalculations} needed are done. T2 position would be recalculated and T3
* position too. When the recalculation happens their dates are modified,
* but in that case we don't want to trigger the dependencies enforcement
* algorithm again. What we want is to record the changes that have happened
* due to the algorithm. When the algorithm ends all notifications are fired
* at once. These notifications are notified to the registered
* {@link INotificationAfterDependenciesEnforcement}.
* </p>
*/
public interface IDependenciesEnforcerHook extends IDependenciesEnforcer {
public void positionPotentiallyModified();
}
public interface IDependenciesEnforcerHookFactory<T> {
/**
* Creates a {@link IDependenciesEnforcerHook} that uses the provided
* {@link INotificationAfterDependenciesEnforcement notifier} to notify
* the changes that have happened due to the algorithm.
*/
public IDependenciesEnforcerHook create(T task,
INotificationAfterDependenciesEnforcement notification);
INotificationAfterDependenciesEnforcement notifier);
public IDependenciesEnforcerHook create(T task);
}
@ -596,6 +627,11 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
}
};
/**
* Tracks all modifications to dates that have happened inside the
* dependencies enforcement algorithm. At the end of the algorithm they're
* executed via {@link DeferedNotifier#doNotifications()}.
*/
public class DeferedNotifier {
private Map<V, NotificationPendingForTask> notificationsPending = new LinkedHashMap<V, NotificationPendingForTask>();
@ -713,6 +749,11 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
private ThreadLocal<DeferedNotifier> deferedNotifier = new ThreadLocal<DeferedNotifier>();
/**
* It creates a {@link IDependenciesEnforcerHook} that starts the
* algorithm <em>onEntrance</em> and in subsequent tasks position
* modifications records the changes <em>onNotification</em>.
*/
@Override
public IDependenciesEnforcerHook create(V task,
INotificationAfterDependenciesEnforcement notificator) {
@ -727,6 +768,10 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
return create(task, EMPTY_NOTIFICATOR);
}
/**
* What to do when a task's position is modified not inside the
* dependencies enforcement algorithm.
*/
private IDependenciesEnforcer onEntrance(final V task) {
return new IDependenciesEnforcer() {
@ -743,6 +788,10 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
};
}
/**
* What to do when a task's position is modified from inside the
* dependencies enforcement algorithm.
*/
private IDependenciesEnforcer onNotification(final V task,
final INotificationAfterDependenciesEnforcement notification) {
return new IDependenciesEnforcer() {
@ -767,6 +816,10 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
}
/**
* Enrich {@link IDependenciesEnforcer} with
* {@link IDependenciesEnforcerHook#positionPotentiallyModified()}.
*/
private IDependenciesEnforcerHook withPositionPotentiallyModified(
final V task, final IDependenciesEnforcer enforcer) {
return new IDependenciesEnforcerHook() {
@ -789,9 +842,18 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
};
}
/**
* Creates a {@link IDependenciesEnforcer} that detects if a position
* change comes from the dependencies algorithm or comes from outside.
* For that a {@link ReentranceGuard} is used. If the dependencies
* enforcement algorithm isn't being executed the
* {@link IDependenciesEnforcer} created delegates to
* <code>onEntrance</code>. Otherwise it delegates to
* <code>notifier</code>.
*/
private IDependenciesEnforcer onlyEnforceDependenciesOnEntrance(
final IDependenciesEnforcer onEntrance,
final IDependenciesEnforcer notification) {
final IDependenciesEnforcer notifier) {
return new IDependenciesEnforcer() {
@Override
@ -806,7 +868,7 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
@Override
public void doAction() {
notification.setStartDate(
notifier.setStartDate(
previousStart,
previousEnd, newStart);
onEntrance.setStartDate(
@ -818,7 +880,7 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
@Override
public void ifAlreadyInside() {
notification.setStartDate(previousStart,
notifier.setStartDate(previousStart,
previousEnd, newStart);
}
@ -837,7 +899,7 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
@Override
public void doAction() {
notification.setNewEnd(previousEnd,
notifier.setNewEnd(previousEnd,
newEnd);
onEntrance.setNewEnd(previousEnd,
newEnd);
@ -847,7 +909,7 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
@Override
public void ifAlreadyInside() {
notification.setNewEnd(previousEnd, newEnd);
notifier.setNewEnd(previousEnd, newEnd);
}
});
}
@ -894,10 +956,20 @@ public class GanttDiagramGraph<V, D extends IDependency<V>> implements
});
}
/**
* When entering and exiting the dependencies enforcement algorithm some
* listeners must be notified.
*/
private void onNewEntrance(final IAction action) {
preAndPostActions.doAction(decorateWithNotifications(action));
}
/**
* Attach the {@link DeferedNotifier} for the current execution.
* {@link DeferedNotifier#doNotifications()} is called once the
* execution has finished, telling all listeners the task positions
* modifications that have happened.
*/
private IAction decorateWithNotifications(final IAction action) {
return new IAction() {

View file

@ -19,6 +19,21 @@
package org.zkoss.ganttz.util;
/**
* <p>
* It marks the start and the end part of a potentially reentering execution
* using a {@link ThreadLocal} variable. For example, some method execution can
* eventually be called again. When that methods is called we want to know if
* it's called within the execution of itself or from the outside. I.e., it's
* useful to do different things depending if the execution is already being
* done or entering in it.
* </p>
*
* <p>
* It can detect if it's already executing or not. If it is,
* {@link IReentranceCases#ifAlreadyInside()} is called, otherwise
* {@link IReentranceCases#ifNewEntrance()} is called.
* </p>
*
* @author Óscar González Fernández <ogfernandez@gmail.com>
*/
public class ReentranceGuard {