diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java index f4c4d8599..3f81d2c58 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java @@ -38,10 +38,8 @@ import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.navalplanner.business.resources.entities.Resource; /** - * - * Entity which represents an element in the queue which represents - * the limiting resources. - * + * Entity which represents an element in the queue which represents the limiting + * resources. * @author Diego Pino Garcia * @author Javier Moran Rua */ @@ -61,11 +59,9 @@ public class LimitingResourceQueueElement extends BaseEntity { private long creationTimestamp; - private Set dependenciesAsOrigin = - new HashSet(); + private Set dependenciesAsOrigin = new HashSet(); - private Set dependenciesAsDestiny = - new HashSet(); + private Set dependenciesAsDestiny = new HashSet(); public static LimitingResourceQueueElement create() { return create(new LimitingResourceQueueElement()); @@ -91,7 +87,8 @@ public class LimitingResourceQueueElement extends BaseEntity { return limitingResourceQueue; } - public void setLimitingResourceQueue(LimitingResourceQueue limitingResourceQueue) { + public void setLimitingResourceQueue( + LimitingResourceQueue limitingResourceQueue) { this.limitingResourceQueue = limitingResourceQueue; } @@ -175,9 +172,10 @@ public class LimitingResourceQueueElement extends BaseEntity { } else if (d.getHasAsDestiny().equals(this)) { dependenciesAsDestiny.add(d); } else { - throw new IllegalArgumentException("It cannot be added a dependency" + - " in which the current queue element is neither origin" + - " not desinty"); + throw new IllegalArgumentException( + "It cannot be added a dependency" + + " in which the current queue element is neither origin" + + " not desinty"); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java index a9246243d..a9da0b43c 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java @@ -60,26 +60,22 @@ public interface ILimitingResourceQueueModel { /** * Assigns a {@link LimitingResourceQueueElement} to its corresponding - * {@link LimitingResourceQueue} - * - * There is one and only one queue for every limiting resource. An element - * is assigned to its queue searching by element.resource. - * - * Allocation within the queue is done by finding the first gap in the queue - * that fits the initial intented hours assigned to - * element.resourceallocation. - * - * The method also generates {@link DayAssignment} once the allocation is - * done - * - * Returns true if the process was successful. The only case were an - * allocation cannot be done is if there's not any queue that can hold the - * element (only for a generic allocation, there's not any queue that - * matches the criteria of the element) - * + * {@link LimitingResourceQueue} There is one and only one queue for every + * limiting resource. An element is assigned to its queue searching by + * element.resource. Allocation within the queue is done by finding the + * first gap in the queue that fits the initial intented hours assigned to + * element.resourceallocation. The method also generates + * {@link DayAssignment} once the allocation is done Returns the inserted + * queue elements. More than one can be inserted because inserting + * element can imply to move its sucessors.
+ * The only case were an allocation cannot be done is if there's not any + * queue that can hold the element (only for a generic allocation, there's + * not any queue that matches the criteria of the element). In this case an + * empty list is returned * @param element */ - boolean assignLimitingResourceQueueElement(LimitingResourceQueueElement element); + List assignLimitingResourceQueueElement( + LimitingResourceQueueElement element); ZoomLevel calculateInitialZoomLevel(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java index ac6f43b4f..5ba5711f3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java @@ -390,9 +390,22 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } @Override - public boolean assignLimitingResourceQueueElement( + public List assignLimitingResourceQueueElement( LimitingResourceQueueElement externalQueueElement) { - GapRequirements requirements = queuesState.getRequirementsFor(externalQueueElement); + List result = new ArrayList(); + for (LimitingResourceQueueElement each : queuesState + .getInsertionsToBeDoneFor(externalQueueElement)) { + GapRequirements requirements = queuesState.getRequirementsFor(each); + boolean inserted = insert(requirements); + if (!inserted) { + break; + } + result.add(requirements.getElement()); + } + return result; + } + + private boolean insert(GapRequirements requirements) { List potentiallyValidGapsFor = queuesState .getPotentiallyValidGapsFor(requirements); boolean generic = requirements.getElement().isGeneric(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java index 5b0af18b1..3f7c0e4d3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java @@ -406,10 +406,16 @@ public class LimitingResourcesController extends GenericForwardComposer { LimitingResourceQueueElementDTO dto) { LimitingResourceQueueElement element = dto.getOriginal(); - if (limitingResourceQueueModel - .assignLimitingResourceQueueElement(element)) { + List inserted = limitingResourceQueueModel + .assignLimitingResourceQueueElement(element); + if (!inserted.isEmpty()) { Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); - limitingResourcesPanel.appendQueueElementToQueue(element); + for (LimitingResourceQueueElement each : inserted) { + // FIXME visually wrong if an element jumps from a queue to + // another + limitingResourcesPanel.refreshQueue(each + .getLimitingResourceQueue()); + } } else { showErrorMessage(_("Cannot allocate selected element. There is not any queue " + "that matches resource allocation criteria at any interval of time")); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java index 7ad9ad282..9b178bccc 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java @@ -23,13 +23,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.Validate; import org.jgrapht.DirectedGraph; +import org.jgrapht.alg.CycleDetector; import org.jgrapht.graph.SimpleDirectedGraph; +import org.jgrapht.traverse.TopologicalOrderIterator; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; @@ -119,6 +122,8 @@ public class QueuesState { LimitingResourceQueueDependency dependency) { LimitingResourceQueueElement origin = dependency.getHasAsOrigin(); LimitingResourceQueueElement destination = dependency.getHasAsDestiny(); + result.addVertex(origin); + result.addVertex(destination); result.addEdge(origin, destination, dependency); } @@ -242,4 +247,87 @@ public class QueuesState { return result; } + /** + * @param externalQueueElement + * the queue element to insert + * @return the list of elements that must be reinserted due to the insertion + * of externalQueueElement + */ + public List getInsertionsToBeDoneFor( + LimitingResourceQueueElement externalQueueElement) { + LimitingResourceQueueElement queueElement = getEquivalent(externalQueueElement); + DirectedGraph subGraph = buildOutgoingGraphFor(queueElement); + CycleDetector cycleDetector = cycleDetector(subGraph); + if (cycleDetector.detectCycles()) { + throw new IllegalStateException("subgraph has cycles"); + } + List result = new ArrayList(); + result.add(queueElement); + result.addAll(getElementsOrderedTopologically(subGraph)); + unassignFromQueues(result); + return result; + } + + private DirectedGraph buildOutgoingGraphFor( + LimitingResourceQueueElement queueElement) { + SimpleDirectedGraph result = instantiateDirectedGraph(); + buildOutgoingGraphFor(result, queueElement); + return result; + } + + private void buildOutgoingGraphFor( + DirectedGraph result, + LimitingResourceQueueElement element) { + Set outgoingEdgesOf = graph + .outgoingEdgesOf(element); + for (LimitingResourceQueueDependency each : outgoingEdgesOf) { + addDependency(result, each); + buildOutgoingGraphFor(result, each.getHasAsDestiny()); + } + } + + private CycleDetector cycleDetector( + DirectedGraph subGraph) { + return new CycleDetector( + subGraph); + } + + private List getElementsOrderedTopologically( + DirectedGraph subGraph) { + return onlyAssigned(toList(topologicalIterator(subGraph))); + } + + private TopologicalOrderIterator topologicalIterator( + DirectedGraph subGraph) { + return new TopologicalOrderIterator( + subGraph); + } + + private static List toList(final Iterator iterator) { + List result = new ArrayList(); + while (iterator.hasNext()) { + result.add(iterator.next()); + } + return result; + } + + private List onlyAssigned( + List list) { + List result = new ArrayList(); + for (LimitingResourceQueueElement each : list) { + if (!each.isDetached()) { + result.add(each); + } + } + return result; + } + + private void unassignFromQueues(List result) { + for (LimitingResourceQueueElement each : result) { + if (!each.isDetached()) { + unassingFromQueue(each); + } + } + } + }