diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index c08e05cc..27574a3f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -87,7 +87,7 @@ public class VehicleRoutingProblem { private FleetSize fleetSize = FleetSize.INFINITE; - private Collection vehicleTypes = new ArrayList(); + private Map vehicleTypes = new HashMap<>(); private Collection initialRoutes = new ArrayList(); @@ -395,8 +395,13 @@ public class VehicleRoutingProblem { incVehicleTypeIdIndexCounter(); } uniqueVehicles.add(vehicle); - if (!vehicleTypes.contains(vehicle.getType())) { - vehicleTypes.add(vehicle.getType()); + if (!vehicleTypes.containsKey(vehicle.getType().getTypeId())) { + vehicleTypes.put(vehicle.getType().getTypeId(), vehicle.getType()); + } else { + VehicleType existingType = vehicleTypes.get(vehicle.getType().getTypeId()); + if (!vehicle.getType().equals(existingType)) { + throw new IllegalArgumentException("A type with type id " + vehicle.getType().getTypeId() + " already exists. However, types are different. Please use unique vehicle types only."); + } } String startLocationId = vehicle.getStartLocation().getId(); addLocationToTentativeLocations(vehicle.getStartLocation()); @@ -495,7 +500,7 @@ public class VehicleRoutingProblem { * @return collection of vehicle-types */ public Collection getAddedVehicleTypes() { - return Collections.unmodifiableCollection(vehicleTypes); + return Collections.unmodifiableCollection(vehicleTypes.values()); } /** @@ -587,7 +592,7 @@ public class VehicleRoutingProblem { this.jobs = builder.jobs; this.fleetSize = builder.fleetSize; this.vehicles = builder.uniqueVehicles; - this.vehicleTypes = builder.vehicleTypes; + this.vehicleTypes = builder.vehicleTypes.values(); this.initialVehicleRoutes = builder.initialRoutes; this.transportCosts = builder.transportCosts; this.activityCosts = builder.activityCosts; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java index 6d430655..df2fdf23 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java @@ -83,4 +83,7 @@ public interface Vehicle extends HasId, HasIndex { // default Object getUserData() { // return null; // }; + public abstract boolean isTaskPermited(String taskId); + + public abstract void addProhibitedTask(String taskId); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java index 0731abf2..41107525 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java @@ -24,7 +24,7 @@ import com.graphhopper.jsprit.core.problem.job.Break; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; +import java.util.*; /** @@ -97,6 +97,16 @@ public class VehicleImpl extends AbstractVehicle { return null; } + @Override + public boolean isTaskPermited(String taskId) { + return true; + } + + @Override + public void addProhibitedTask(String taskId) { + throw new IllegalArgumentException("NoVehicle should not have prohibited tasks"); + } + } /** @@ -134,6 +144,8 @@ public class VehicleImpl extends AbstractVehicle { private Object userData; + private Set prohibitedTasks = new HashSet<>(); + private Builder(String id) { super(); this.id = id; @@ -248,6 +260,12 @@ public class VehicleImpl extends AbstractVehicle { return this; } + public Builder addExcludedTask(String taskId){ + if (taskId != null) + prohibitedTasks.add(taskId); + return this; + } + /** * Builds and returns the vehicle. *

@@ -331,6 +349,8 @@ public class VehicleImpl extends AbstractVehicle { private final Break aBreak; + private final Set prohibitedTasks; + private VehicleImpl(Builder builder) { setUserData(builder.userData); id = builder.id; @@ -342,6 +362,7 @@ public class VehicleImpl extends AbstractVehicle { endLocation = builder.endLocation; startLocation = builder.startLocation; aBreak = builder.aBreak; + prohibitedTasks = builder.prohibitedTasks; // setVehicleIdentifier(new VehicleTypeKey(type.getTypeId(),startLocation.getId(),endLocation.getId(),earliestDeparture,latestArrival,skills)); setVehicleIdentifier(new VehicleTypeKey(type.getTypeId(), startLocation.getId(), endLocation.getId(), earliestDeparture, latestArrival, skills, returnToDepot)); } @@ -407,6 +428,16 @@ public class VehicleImpl extends AbstractVehicle { return aBreak; } + @Override + public boolean isTaskPermited(String taskId) { + return !prohibitedTasks.contains(taskId); + } + + @Override + public void addProhibitedTask(String taskId) { + prohibitedTasks.add(taskId); + } + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java index 112c137c..931d6187 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java @@ -81,6 +81,37 @@ public class VehicleTypeImpl implements VehicleType { public String toString() { return "[fixed=" + fix + "][perTime=" + perTransportTimeUnit + "][perDistance=" + perDistanceUnit + "][perWaitingTimeUnit=" + perWaitingTimeUnit + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleCostParams)) return false; + + VehicleCostParams that = (VehicleCostParams) o; + + if (Double.compare(that.fix, fix) != 0) return false; + if (Double.compare(that.perTransportTimeUnit, perTransportTimeUnit) != 0) return false; + if (Double.compare(that.perDistanceUnit, perDistanceUnit) != 0) return false; + if (Double.compare(that.perWaitingTimeUnit, perWaitingTimeUnit) != 0) return false; + return Double.compare(that.perServiceTimeUnit, perServiceTimeUnit) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(fix); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perTransportTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perDistanceUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perWaitingTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perServiceTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } /** @@ -299,32 +330,30 @@ public class VehicleTypeImpl implements VehicleType { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((typeId == null) ? 0 : typeId.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleTypeImpl)) return false; + + VehicleTypeImpl that = (VehicleTypeImpl) o; + + if (Double.compare(that.maxVelocity, maxVelocity) != 0) return false; + if (!typeId.equals(that.typeId)) return false; + if (profile != null ? !profile.equals(that.profile) : that.profile != null) return false; + if (!vehicleCostParams.equals(that.vehicleCostParams)) return false; + return capacityDimensions.equals(that.capacityDimensions); } - /** - * Two vehicle-types are equal if they have the same vehicleId. - */ @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VehicleTypeImpl other = (VehicleTypeImpl) obj; - if (typeId == null) { - if (other.typeId != null) - return false; - } else if (!typeId.equals(other.typeId)) - return false; - return true; + public int hashCode() { + int result; + long temp; + result = typeId.hashCode(); + result = 31 * result + (profile != null ? profile.hashCode() : 0); + result = 31 * result + vehicleCostParams.hashCode(); + result = 31 * result + capacityDimensions.hashCode(); + temp = Double.doubleToLongBits(maxVelocity); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } private final String typeId; diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java index 75e117a5..ac7e4c97 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java @@ -316,6 +316,17 @@ public class VehicleRoutingProblemTest { builder.addVehicle(vehicle2); } + @Test(expected = IllegalArgumentException.class) + public void whenAddingVehicleTypesWithSameIdButDifferentCosts_itShouldThrowException() { + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType type2 = VehicleTypeImpl.Builder.newInstance("type").setCostPerServiceTime(2d).build(); + VehicleImpl vehicle1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance("loc")).setType(type1).build(); + VehicleImpl vehicle2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance("loc")).setType(type2).build(); + builder.addVehicle(vehicle1); + builder.addVehicle(vehicle2); + } + @Test(expected = IllegalArgumentException.class) public void whenBuildingProblemWithSameBreakId_itShouldThrowException(){ VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java index 0c85b96a..bd145479 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java @@ -124,6 +124,7 @@ public class VehicleTypeImplTest { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(-10).build(); } + @Test public void whenSettingPerDistanceCosts_itShouldBeSetCorrectly() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(10).build(); assertEquals(10.0, type.getVehicleCostParams().perDistanceUnit, 0.0); @@ -166,4 +167,32 @@ public class VehicleTypeImplTest { assertEquals(42, two.getUserData()); assertNull(three.getUserData()); } + + @Test + public void typesShouldBeEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertTrue(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 20).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + assertTrue(one.equals(two)); + } }