/** * */ package vroom.optimization.online.jmsa.vrp; import java.util.ArrayList; import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.concurrent.TimeUnit; import vroom.common.modeling.dataModel.ListRoute.ArrayListRoute; import vroom.common.modeling.dataModel.INodeVisit; import vroom.common.modeling.dataModel.IRoute; import vroom.common.modeling.dataModel.IVRPRequest; import vroom.common.modeling.dataModel.IVRPSolution; import vroom.common.modeling.dataModel.NodeInsertion; import vroom.common.modeling.dataModel.RouteBase; import vroom.common.modeling.dataModel.Vehicle; import vroom.common.utilities.Constants; import vroom.common.utilities.ExtendedReentrantLock; import vroom.common.utilities.IDerefenceable; import vroom.common.utilities.ILockable; import vroom.common.utilities.IObservable; import vroom.common.utilities.IObserver; import vroom.common.utilities.ObserverProxy; import vroom.common.utilities.Update; import vroom.common.utilities.Utilities; import vroom.common.utilities.ValueUpdate; import vroom.common.utilities.dataModel.IDHelper; import vroom.common.utilities.dataModel.IObjectWithID; import vroom.optimization.online.jmsa.IActualRequest; import vroom.optimization.online.jmsa.ISampledRequest; import vroom.optimization.online.jmsa.utils.MSALogging; /** * Creation date: Apr 6, 2010 - 3:03:52 PM<br/> * <code>VRPScenarioRoute</code> is an implementation of {@link IRoute} that wrap another instance of {@link IRoute} * that represent the <em>variable</em> part of the route, while adding information about the <em>fixed</em> part of the * route that correspond to the nodes that have already been visited. * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class VRPScenarioRoute implements IRoute<VRPRequest>, ILockable, IObjectWithID, IObserver, Cloneable, IDerefenceable { private final ObserverProxy mObserverProxy; public static enum State { NOT_STARTED, STARTED, TERMINATED, DELETED }; /** * The wrapped instance of {@link IRoute} */ private final RouteBase mRoute; /** The last request that was fixed */ private VRPActualRequest mLastFixedRequest; /** The served status of the last fixed visit **/ private boolean mLastRequestServed; /** An unique ID for this scenario */ private final int mID; /** A flag set to true if this route contains a shrunk node */ private boolean mShrunkNode; /** The current state of this route **/ private State mCurrentState; /** * Getter for currentState : The current state of this route * * @return the value of currentState */ public State getCurrentState() { return mCurrentState; } /** * Setter for currentState : The current state of this route * * @param currentState * the value to be set for currentState */ public void setCurrentState(State currentState) { mCurrentState = currentState; } /** * <code>true</code> if this route should be added as observer each time a request is added */ private boolean mAddAsObserver; /** * Getter for the <code>addAsObserver</code> flag. * * @return <code>true</code> if this route should be added as observer each time a request is added */ public boolean isAddAsObseverEnabled() { return mAddAsObserver; } /** * Sets the <code>addAsObserver</code> flag. * <p> * This flag should be disabled for performance when initializing or optimizing a route * </p> * <p> * Note that setting the <code>addAsObserver</code> flag to <code>false</code> will unregister this instance from * all requests, while setting it to <code>true</code> will register it. * * @param addAsObserver * <code>true</code> if this route should be added as observer each time a request is added */ public void setAddAsObserver(boolean addAsObserver) { checkLock(); mAddAsObserver = addAsObserver; for (VRPRequest i : this) { if (mAddAsObserver) { i.addObserver(mObserverProxy); } else { i.removeObserver(mObserverProxy); } } } /** * Creates a new <code>VRPScenarioRoute</code> based on an {@link ArrayListRoute} * <p/> * This instance will use the same lock contained in the parent VRPScenario * * @param parentScenario * the parent scenario * @param vehicle * the vehicle to which this route will be associated */ public VRPScenarioRoute(VRPScenario parentScenario, Vehicle vehicle) { this(ArrayListRoute.class, parentScenario, vehicle); setAddAsObserver(false); } /** * Creates a new <code>VRPScenarioRoute</code> * <p/> * This instance will use the same lock contained in the parent VRPScenario * * @param routeClass * the type of route to be used as backup for this instance * @param parentScenario * the parent scenario * @param vehicle * the vehicle to which this route will be associated */ public VRPScenarioRoute(Class<? extends RouteBase> routeClass, VRPScenario parentScenario, Vehicle vehicle) { // Optimization for knwon route type if (routeClass == ArrayListRoute.class) { mRoute = new ArrayListRoute(parentScenario, vehicle); } else { mRoute = Utilities.newInstance(routeClass, parentScenario, vehicle); } mLastFixedRequest = null; setLastRequestServed(true); mID = IDHelper.getNextId(this.getClass()); mCurrentState = State.NOT_STARTED; setAddAsObserver(false); mObserverProxy = new ObserverProxy(this); } /** * Creates a new <code>VRPScenarioRoute</code> for cloning operations * * @param route */ private VRPScenarioRoute(RouteBase route) { mRoute = route; mLastFixedRequest = null; setLastRequestServed(true); mID = IDHelper.getNextId(this.getClass()); mCurrentState = State.NOT_STARTED; setAddAsObserver(false); mObserverProxy = new ObserverProxy(this); } /** * Creates a new <code>VRPScenarioRoute</code> associated with the given scenario and copying the node sequence * contained in the given route * * @param parentScenario * the parent scenario for this instance * @param route * the route which node sequence will be copied */ public VRPScenarioRoute(VRPScenario parentScenario, VRPScenarioRoute route) { this(parentScenario, route.getVehicle()); mRoute.appendRoute(route); mShrunkNode = route.mShrunkNode; mCurrentState = route.mCurrentState; setAddAsObserver(false); } /** * Getter for the first actual visit (possibly a depot) * * @return the visit corresponding to the first {@link IActualRequest} of this route */ public VRPActualRequest getFirstActualRequest() { // checkLock(); int ind = 0; for (VRPRequest node : this) { if (node instanceof VRPActualRequest && (// getCurrentState()==State.NOT_STARTED !node.isDepot() || ind > 0)) { internalReleaseLock(); return (VRPActualRequest) node; } ind++; } // internalReleaseLock(); return null; } /** * Getter for the lastRequestServed flag * * @return the served status of the last fixed visit: <code>true</code> if the last request has been served */ public boolean isLastFixedRequestServed() { return mLastRequestServed; } /** * Set the lastRequestServed flag * * @param lastRequestServed */ private void setLastRequestServed(boolean lastRequestServed) { mLastRequestServed = lastRequestServed; } /** * Fix the first actual request of this route * * @return the {@link VRPRequest} corresponding to the first actual request or depot */ public VRPActualRequest fixFirstActualRequest() { VRPActualRequest firstActualVisit = null; if (getCurrentState() == State.TERMINATED) { throw new IllegalStateException("Route is already terminated"); } if (!containsShrunkNode()) { throw new IllegalStateException( "Cannot fix the first actual request on a route that does not contain a shrunk node"); } checkLock(); if (length() > 1) { if (!isLastFixedRequestServed()) { MSALogging .getComponentsLogger() .warn("VRPScenarioRoute.fixFirstActualRequest: Should not fix the first actual visit until the last fixed visit has been served (last visit: %s)", getLastFixedRequest()); } Iterator<VRPRequest> it = iterator(); int ind = 0; int start = 0; while (it.hasNext() && firstActualVisit == null) { VRPRequest v = it.next(); if (v.isDepot() && ind == 0 || v instanceof VRPShrunkRequest) { start++; // Ignore the first depot and possible shrunk node } if (v instanceof VRPActualRequest && (!v.isDepot() || ind > 0)) { firstActualVisit = (VRPActualRequest) v; } else { ind++; } } if (start > 0 && start <= ind) { mLastFixedRequest = firstActualVisit; setLastRequestServed(false); if (start == length() - 1) { mCurrentState = State.TERMINATED; } extractNodes(start, ind); // Update the cost: shrunk node [---AB] has the same location as // B // Route was 0[---A]BC--- is now 0[---AB]C--- // extractNodes update the cost by delta=-cBB-cBC+cBC // need to update cost by delta=-cAB-c0A+c0B VRPShrunkRequest shrunk = getParentSolution().getParentInstance() .getShrunkRequest(getVehicle().getID()); List<VRPActualRequest> nodes = shrunk.getShrunkNodes(); if (nodes.size() >= 2) { VRPActualRequest prelast = nodes.get(nodes.size() - 2); VRPActualRequest last = nodes.get(nodes.size() - 1); double remArcCost = -mRoute.getArcCost(prelast, last) - mRoute.getArcCost(getFirstNode(), prelast) + mRoute.getArcCost(getFirstNode(), last); updateCost(remArcCost); } internalReleaseLock(); return firstActualVisit; } else { internalReleaseLock(); return null; } } else { internalReleaseLock(); return null; } } /** * Getter for the last fixed visit * * @return the last visit that was fixed */ public VRPActualRequest getLastFixedRequest() { // checkLock(); VRPActualRequest r = mLastFixedRequest; // internalReleaseLock(); return r; } /** * Sets the lastRequestServed flag to <code>true</code> * * @link {@link #isLastFixedRequestServed()} * @return <code>true</code> if the last visit was not already marked as served */ public boolean markLastRequestAsServed() { checkLock(); boolean prevVal = isLastFixedRequestServed(); setLastRequestServed(true); internalReleaseLock(); return !prevVal; } /** * Ordered list of actual visits * * @return a list containing the visits corresponding to {@link IActualRequest} in the order in which they appear in * the route */ public List<VRPActualRequest> getOrderedActualRequests() { checkLock(); List<VRPActualRequest> requests = new ArrayList<VRPActualRequest>(); int ind = 0; for (VRPRequest v : this) { if (v instanceof VRPActualRequest && (getCurrentState() == State.NOT_STARTED || !v.isDepot() || ind > 0)) { requests.add((VRPActualRequest) v); } ind++; } internalReleaseLock(); return requests; } /** * Ordered list of sampled visits * * @return a list containing the visits corresponding to {@link ISampledRequest} in the order in which they appear * in the route */ public List<VRPSampledRequest> getOrderedSampledRequests() { checkLock(); List<VRPSampledRequest> requests = new ArrayList<VRPSampledRequest>(); for (VRPRequest v : this) { if (v instanceof VRPSampledRequest) { requests.add((VRPSampledRequest) v); } } internalReleaseLock(); return requests; } /** * @param node * the node to be checked * @param added * <code>true</code> if the node was added, <code>false</code> if it was removed */ protected void checkNode(VRPRequest node, boolean added) { if (node instanceof VRPShrunkRequest) { if (added) { if (mShrunkNode) { throw new IllegalStateException("This route already contains a shrunk node"); } mShrunkNode = true; } else { mShrunkNode = false; } } if (added && isAddAsObseverEnabled()) { node.addObserver(mObserverProxy); } else { node.removeObserver(mObserverProxy); } } /** * Getter for the shrunk node presence flag * * @return <code>true</code> if this route contains a shrunk node at any position */ public boolean containsShrunkNode() { return mShrunkNode; } /** * Ensure that the shrunk node is the first visited node. * * @return <code>true</code> if the route has a coherent sequence */ public boolean ensureRouteSequence() { boolean coherent = false; if (containsShrunkNode()) { if (getNodeAt(1) instanceof VRPShrunkRequest) { // The first node is the shrunk node coherent = true; } else if (getNodeAt(length() - 2) instanceof VRPShrunkRequest) { // The last node is the shrunk node reverseRoute(); coherent = true; } else { // The shrunk node is neither in first nor in last position coherent = false; } } else { coherent = true; } return coherent; } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#appendNode(vroom.common.modeling * .dataModel.VRPRequest) */ @Override public boolean appendNode(VRPRequest node) { checkLock(); checkNode(node, true); boolean r = mRoute.appendNode(node); getParentSolution().nodeAdded(this, node); internalReleaseLock(); return r; } @Override public boolean appendRoute(IRoute<? extends VRPRequest> appendedRoute) { checkLock(); boolean r = mRoute.appendRoute(appendedRoute); if (r) { for (VRPRequest node : appendedRoute) { checkNode(node, true); getParentSolution().nodeAdded(this, node); } } internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.IRoute#append(java.util.List) */ @Override public boolean appendNodes(List<? extends VRPRequest> nodes) { checkLock(); boolean r = mRoute.appendNodes(nodes); if (r) { for (VRPRequest node : nodes) { checkNode(node, true); getParentSolution().nodeAdded(this, node); } } internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#bestInsertion(edu.uniandes.copa * .VroomModelling.dataModel.VRPRequest) */ @Override public boolean bestInsertion(VRPRequest node) { checkLock(); boolean r = mRoute.bestInsertion(node); if (r) { checkNode(node, true); getParentSolution().nodeAdded(this, node); } internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#calculateCost(boolean) */ @Override public void calculateCost(boolean force) { // checkLock(); mRoute.calculateCost(force); // internalReleaseLock(); } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#calculateRemaingCapacities(boolean * ) */ @Override public void calculateLoad(boolean force) { // checkLock(); mRoute.calculateLoad(force); // internalReleaseLock(); } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#canAccommodateRequest(edu.uniandes * .copa.VroomModelling.dataModel.Request) */ @Override public boolean canAccommodateRequest(IVRPRequest request) { // checkLock(); boolean r = mRoute.canAccommodateRequest(request); // internalReleaseLock(); return r; } /** * Insertion viability test * * @param request * @return <code>true</code> if the given request can be inserted in this route */ public boolean canAccommodateRequest(VRPRequest request) { // checkLock(); calculateLoad(true); for (int p = 0; p < getVehicle().getCompartmentCount(); p++) { if (getLoad(p) + request.getDemand(p) > getVehicle().getCapacity(p)) { internalReleaseLock(); return false; } } // internalReleaseLock(); return true; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#extractNode(int) */ @Override public VRPRequest extractNode(int index) { checkLock(); VRPRequest node = (VRPRequest) mRoute.extractNode(index); checkNode(node, false); getParentSolution().nodeRemoved(this, node); internalReleaseLock(); return node; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.IRoute#extractSubRoute(int, int) */ @Override public VRPScenarioRoute extractSubroute(int start, int end) { checkLock(); RouteBase route = mRoute.extractSubroute(start, end); for (INodeVisit node : route) { checkNode((VRPRequest) node, false); getParentSolution().nodeRemoved(this, (VRPRequest) node); } internalReleaseLock(); return new VRPScenarioRoute(route); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#extractSubroute(int, int) */ @Override public List<VRPRequest> extractNodes(int start, int end) { checkLock(); List<VRPRequest> list = Utilities.convertToList(mRoute.extractNodes(start, end)); for (VRPRequest node : list) { checkNode(node, false); getParentSolution().nodeRemoved(this, node); } internalReleaseLock(); return list; } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#getBestNodeInsertion(edu.uniandes * .copa.VroomModelling.dataModel.Node) */ @Override public NodeInsertion getBestNodeInsertion(INodeVisit node) { checkLock(); NodeInsertion rInt = mRoute.getBestNodeInsertion(node); internalReleaseLock(); return new NodeInsertion(node, rInt.getCost(), rInt.getPosition(), this); } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.IRoute#getBestNodeInsertion(edu.uniandes * .copa.VroomModelling.dataModel.Node, int, int) */ @Override public NodeInsertion getBestNodeInsertion(INodeVisit node, int min, int max) { checkLock(); NodeInsertion rInt = mRoute.getBestNodeInsertion(node, min, max); internalReleaseLock(); return new NodeInsertion(node, rInt.getCost(), rInt.getPosition(), this); } /** * Best insertion of a request * <p> * This method assumes that the route is * </p> * * @param request * the request to be inserted * @return the best insertion for the given request, or <code>null</code> if this route cannot accommodate the given * request * @see VRPScenarioRoute#getBestNodeInsertion(INodeVisit, int, int) * @see #canAccommodateRequest(VRPRequest) */ public NodeInsertion getBestRequestInsertion(VRPRequest request) { if (!canAccommodateRequest(request)) { return null; } if (ensureRouteSequence()) { int min = 0, max = length(); if (getLastNode().isDepot()) { max -= 1; } if (getFirstNode().isDepot()) { min += 1; } if (containsShrunkNode()) { min += 1; } return getBestNodeInsertion(request, min, max); } else { return null; } } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getCost() */ @Override public double getCost() { return mRoute.getCost(); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getFirstNode() */ @Override public VRPRequest getFirstNode() { // checkLock(); VRPRequest r = (VRPRequest) mRoute.getFirstNode(); // internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getLastNode() */ @Override public VRPRequest getLastNode() { // checkLock(); VRPRequest r = (VRPRequest) mRoute.getLastNode(); // internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getNodeAt(int) */ @Override public VRPRequest getNodeAt(int index) { // checkLock(); VRPRequest r = (VRPRequest) mRoute.getNodeAt(index); // internalReleaseLock(); return r; } @Override public int getNodePosition(INodeVisit node) { int i = 0; for (INodeVisit n : this) { if (n.getID() == node.getID()) { return i; } i++; } return -1; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getNodeSequence() */ @Override public List<VRPRequest> getNodeSequence() { // checkLock(); return Utilities.convertToList(mRoute.getNodeSequence()); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getParentSolution() */ @Override public VRPScenario getParentSolution() { return (VRPScenario) mRoute.getParentSolution(); } /** * Changes the parent solution of this route. * <p> * Useful to copy a route to a new solution without copying the actual information * </p> * <p> * Warning: This method will remove this route from the current parent solution, but will not add it to the new one * </p> * * @param parentSolution * the new parent solution */ protected void changeParentSolution(IVRPSolution<?> parentSolution) { mRoute.changeParentSolution(parentSolution); } @Override public double[] getLoads() { // double[] loads = mRoute.getLoads(); // for (int p = 0; p < loads.length; p++) // loads[p] += getParentSolution().getParentInstance().getCurrentLoad( // getVehicle().getID(), p); // return loads; return mRoute.getLoads(); } @Override public double getLoad() { return mRoute.getLoad(0); } @Override public double getLoad(int product) { return mRoute.getLoad(product); // + getParentSolution().getParentInstance().getCurrentLoad( // getVehicle().getID(), product); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#getVehicle() */ @Override public Vehicle getVehicle() { return mRoute.getVehicle(); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#insertNode(int, * vroom.common.modeling.dataModel.VRPRequest) */ @Override public boolean insertNode(int index, VRPRequest node) { checkLock(); checkNode(node, true); boolean r = mRoute.insertNode(index, node); getParentSolution().nodeAdded(this, node); internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see * vroom.common.modeling.dataModel.Route#insertNode(vroom.common.modeling * .dataModel.RouteBase.NodeInsertion, * vroom.common.modeling.dataModel.VRPRequest) */ @Override public boolean insertNode(NodeInsertion ins, VRPRequest node) { checkLock(); checkNode(node, true); boolean r = mRoute.insertNode(ins, node); getParentSolution().nodeAdded(this, node); internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#insertSubroute(int, * java.util.List) */ @Override public boolean insertNodes(int index, List<? extends VRPRequest> subroute) { checkLock(); for (VRPRequest node : subroute) { checkNode(node, true); } boolean r = mRoute.insertNodes(index, subroute); for (VRPRequest node : subroute) { getParentSolution().nodeAdded(this, node); } internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.IRoute#insertSubroute(int, * vroom.common.modeling.dataModel.IRoute) */ @Override public boolean insertSubroute(int index, IRoute<? extends VRPRequest> subroute) { checkLock(); for (VRPRequest node : subroute) { checkNode(node, true); } boolean r = mRoute.insertSubroute(index, subroute); for (VRPRequest node : subroute) { getParentSolution().nodeAdded(this, node); } internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#length() */ @Override public int length() { return mRoute.length(); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#revertSubRoute(int, int) */ @Override public void reverseSubRoute(int start, int end) { checkLock(); mRoute.reverseSubRoute(start, end); internalReleaseLock(); } @Override public void reverseRoute() { checkLock(); mRoute.reverseRoute(); internalReleaseLock(); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#setNodeAt(int, * vroom.common.modeling.dataModel.VRPRequest) */ @Override public VRPRequest setNodeAt(int index, VRPRequest node) { checkLock(); VRPRequest prev = (VRPRequest) mRoute.setNodeAt(index, node); checkNode(prev, false); checkNode(node, true); getParentSolution().nodeAdded(this, node); getParentSolution().nodeRemoved(this, node); internalReleaseLock(); return prev; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#subroute(int, int) */ @Override public List<VRPRequest> subroute(int start, int end) { checkLock(); List<VRPRequest> r = Utilities.convertToList(mRoute.subroute(start, end)); internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#swapNodes(int, int) */ @Override public boolean swapNodes(int node1, int node2) { checkLock(); boolean r = mRoute.swapNodes(node1, node2); internalReleaseLock(); return r; } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.Route#updateCost(double) */ @Override public void updateCost(double delta) { checkLock(); mRoute.updateCost(delta); internalReleaseLock(); } /* * (non-Javadoc) * * @see vroom.common.modeling.dataModel.IRoute#updateLoad(int, double) */ @Override public void updateLoad(int product, double delta) { checkLock(); mRoute.updateLoad(product, delta); internalReleaseLock(); } /* * (non-Javadoc) * * @see java.lang.Iterable#iterator() */ @Override public ListIterator<VRPRequest> iterator() { return Utilities.castIterator(mRoute.iterator()); } @Override public String getNodeSeqString() { StringBuilder b = new StringBuilder(length() * 4); b.append("<"); Iterator<VRPRequest> it2 = iterator(); while (it2.hasNext()) { b.append(it2.next().toShortString()); if (it2.hasNext()) { b.append(','); } } b.append('>'); return b.toString(); } @Override public synchronized String toString() { boolean l = tryLock(TRY_LOCK_TIMOUT); if (l) { StringBuilder b = new StringBuilder(length() * 5); b.append(String.format("cost:%.2f", getCost())); b.append(" length:"); b.append(length()); b.append(" load:"); b.append(Arrays.toString(getLoads())); b.append(" "); b.append(getNodeSeqString()); releaseLock(); return b.toString(); } else { return Constants.TOSTRING_LOCKED; } } @Override public VRPScenarioRoute clone() { VRPScenarioRoute clone = new VRPScenarioRoute(mRoute.clone()); clone.setLastRequestServed(mLastRequestServed); clone.mShrunkNode = mShrunkNode; clone.mLastFixedRequest = mLastFixedRequest; clone.mCurrentState = mCurrentState; return clone; } /** * Clone this route with a reference to the cloned parent scenario. * <p> * This method should only be used when cloning a scenario * </p> * * @param clonedScenario * the clone of this route parent scenario * @return a clone of this route associated with the cloned scenario */ protected VRPScenarioRoute cloneInternal(VRPScenario clonedScenario) { VRPScenarioRoute clone = new VRPScenarioRoute(mRoute.getClass(), clonedScenario, getVehicle()); clone.mRoute.appendRoute(mRoute); clone.setLastRequestServed(mLastRequestServed); clone.mShrunkNode = mShrunkNode; clone.mLastFixedRequest = mLastFixedRequest; clone.mCurrentState = mCurrentState; return clone; } @Override public int getID() { return mID; } // ------------------------------------ // ILockable interface implementation // ------------------------------------ @Override public boolean tryLock(long timeout) { try { return getLockInstance().tryLock(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return false; } } @Override public void acquireLock() { try { if (!getParentSolution().getLockInstance().tryLock(TRY_LOCK_TIMOUT, TRY_LOCK_TIMOUT_UNIT)) { throw new IllegalStateException(String.format( "Unable to acquire lock on this instance of %s (%s) after %s %s, owner: %s", this.getClass() .getSimpleName(), hashCode(), TRY_LOCK_TIMOUT, TRY_LOCK_TIMOUT_UNIT, getLockInstance() .getOwnerName())); } } catch (InterruptedException e) { throw new IllegalStateException(String.format("Unable to acquire lock on this instance of %s (%s)", this .getClass().getSimpleName(), hashCode()), e); } ; } @Override public void releaseLock() { getParentSolution().getLockInstance().unlock(); } protected void internalReleaseLock() { getParentSolution().internalReleaseLock(); } @Override public boolean isLockOwnedByCurrentThread() { return getParentSolution().getLockInstance().isHeldByCurrentThread(); } @Override public ExtendedReentrantLock getLockInstance() { return getParentSolution().getLockInstance(); } private void checkLock() throws ConcurrentModificationException { getParentSolution().checkLock(); } /* * (non-Javadoc) * * @see * vroom.common.utilities.IObserver#update(vroom.common.utilities.IObservable * , java.lang.Object) */ @Override public void update(IObservable source, Update update) { if (update instanceof ValueUpdate && VRPRequest.PROP_DEMANDS.equals(((ValueUpdate) update).getDescription())) { // acquireLock(); for (int p = 0; p < getVehicle().getCompartmentCount(); p++) { mRoute.updateLoad(p, -((double[]) ((ValueUpdate) update).getOldValue())[p] + ((double[]) ((ValueUpdate) update).getNewValue())[p]); } // releaseLock(); } } @Override public boolean contains(INodeVisit node) { return mRoute.contains(node); } @Override public boolean remove(INodeVisit node) { boolean b = mRoute.remove(node); if (b && node instanceof VRPRequest) { getParentSolution().nodeRemoved(this, (VRPRequest) node); checkNode((VRPRequest) node, false); } return b; } @Override public void dereference() { for (VRPRequest i : this) { i.removeObserver(mObserverProxy); if (i instanceof IDerefenceable) { ((IDerefenceable) i).dereference(); } } mObserverProxy.detach(); mCurrentState = State.DELETED; } }