diff --git a/src/main/java/agency/AbstractVehicle.java b/src/main/java/agency/AbstractVehicle.java new file mode 100644 index 0000000000000000000000000000000000000000..b84af77254c843db5fd7fc486fd4d9894e0fdf98 --- /dev/null +++ b/src/main/java/agency/AbstractVehicle.java @@ -0,0 +1,54 @@ +package agency; + +import util.TimeProvider; + +public abstract class AbstractVehicle implements Vehicle { + protected String brand; + protected String model; + protected int productionYear; + + protected static final int MINIMAL_YEAR_VALUE = 1900; + + public AbstractVehicle(String brand, String model, int productionYear) { + int currentYear = TimeProvider.currentYearValue(); + if (productionYear < MINIMAL_YEAR_VALUE || productionYear > currentYear) { + throw new IllegalArgumentException("Invalid production year: " + productionYear); + } + this.brand = brand; + this.model = model; + this.productionYear = productionYear; + } + + @Override + public String getBrand() { + return brand; + } + + @Override + public String getModel() { + return model; + } + + @Override + public int getProductionYear() { + return productionYear; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractVehicle that = (AbstractVehicle) o; + + return productionYear == that.productionYear && + brand.equals(that.brand) && + model.equals(that.model); + } + + @Override + public abstract String toString(); + + @Override + public abstract double dailyRentalPrice(); +} \ No newline at end of file diff --git a/src/main/java/agency/App.java b/src/main/java/agency/App.java index fdd6a397c562be765ea6826b2102797f872237fe..71857b84a944befab9818c4a1313601dcfa00a65 100644 --- a/src/main/java/agency/App.java +++ b/src/main/java/agency/App.java @@ -1,7 +1,52 @@ package agency; +import java.util.List; + public class App { - public static void main(String[] args){ + public static void main(String[] args) { + Vehicle car1 = new Car("Toyota", "Corolla", 2020, 5); // Prix : 200.0€ + Vehicle car2 = new Car("Ford", "Focus", 2018, 4); // Prix : 160.0€ + Vehicle car3 = new Car("Toyota", "Yaris", 2015, 3); // Prix : 60.0€ + + RentalAgency agency = new RentalAgency(List.of(car1, car2, car3)); + + Client client1 = new Client("John", "Doe", 1990); + Client client2 = new Client("Anna", "Smith", 1985); + + System.out.println("Véhicules disponibles dans l'agence :"); + agency.getVehicles().forEach(System.out::println); + + try { + double price = agency.rentVehicle(client1, car1); + System.out.println(client1.getFirstName() + " " + client1.getLastName() + " a loué " + car1 + " pour " + price + "€."); + } catch (Exception e) { + System.out.println("Erreur lors de la location : " + e.getMessage()); + } + + System.out.println("Car1 est-il loué ? " + agency.vehicleIsRented(car1)); // true + + try { + agency.rentVehicle(client2, car1); + } catch (Exception e) { + System.out.println("Erreur pour " + client2.getFirstName() + " : " + e.getMessage()); + } + + try { + double price = agency.rentVehicle(client2, car2); + System.out.println(client2.getFirstName() + " " + client2.getLastName() + " a loué " + car2 + " pour " + price + "€."); + } catch (Exception e) { + System.out.println("Erreur lors de la location : " + e.getMessage()); + } + + System.out.println("Véhicules actuellement loués :"); + agency.allRentedVehicles().forEach(System.out::println); + + agency.returnVehicle(client1); + System.out.println(client1.getFirstName() + " " + client1.getLastName() + " a rendu sa voiture."); + + System.out.println("Car1 est-il encore loué ? " + agency.vehicleIsRented(car1)); // false + System.out.println("Véhicules actuellement loués après retour :"); + agency.allRentedVehicles().forEach(System.out::println); } -} +} \ No newline at end of file diff --git a/src/main/java/agency/BrandCriterion.java b/src/main/java/agency/BrandCriterion.java new file mode 100644 index 0000000000000000000000000000000000000000..6f235409092e6490b71ff1a2b635afde3c37dac5 --- /dev/null +++ b/src/main/java/agency/BrandCriterion.java @@ -0,0 +1,26 @@ +import agency.Vehicle; + +import java.util.function.Predicate; + +public class BrandCriterion implements Predicate<Vehicle> { + private final String brand; + + public BrandCriterion(String brand) { + if (brand == null || brand.isEmpty()) { + throw new IllegalArgumentException("The brand cannot be null or empty."); + } + this.brand = brand; + } + + @Override + public boolean test(Vehicle vehicle) { + if (vehicle == null) { + return false; + } + return brand.equals(vehicle.getBrand()); + } + + public String getBrand() { + return brand; + } +} \ No newline at end of file diff --git a/src/main/java/agency/Car.java b/src/main/java/agency/Car.java new file mode 100644 index 0000000000000000000000000000000000000000..428f6b62cedd27c560bc86541d51af5cc130b3d3 --- /dev/null +++ b/src/main/java/agency/Car.java @@ -0,0 +1,68 @@ +package agency; + +public class Car implements Vehicle { + private String brand; + private String model; + private int productionYear; + private VehiclesSpecifics specifics; + + public Car(String brand, String model, int productionYear, int numberOfSeats) { + if (productionYear < 1900 || productionYear > util.TimeProvider.currentYearValue()) { + throw new IllegalArgumentException("Invalid production year: " + productionYear); + } + + this.brand = brand; + this.model = model; + this.productionYear = productionYear; + this.specifics = new CarSpecifics(numberOfSeats, productionYear); + } + + @Override + public String getBrand() { + return brand; + } + + @Override + public String getModel() { + return model; + } + + @Override + public int getProductionYear() { + return productionYear; + } + + public int getNumberOfSeats() { + return ((CarSpecifics) specifics).getNumberOfSeats(); + } + + @Override + public double dailyRentalPrice() { + return specifics.calculateDailyRentalPrice(); + } + + public boolean isNew() { + if (specifics instanceof CarSpecifics) { + return ((CarSpecifics) specifics).isNew(); + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Car car = (Car) o; + + return productionYear == car.productionYear && + brand.equals(car.brand) && + model.equals(car.model); + } + + @Override + public String toString() { + return "Car " + brand + " " + model + " " + productionYear + + " (" + specifics.getDetails() + ") : " + dailyRentalPrice() + "€"; + } +} \ No newline at end of file diff --git a/src/main/java/agency/CarSpecifics.java b/src/main/java/agency/CarSpecifics.java new file mode 100644 index 0000000000000000000000000000000000000000..1f6a743b8a79c9cb84835c72d1fbb92118d974ec --- /dev/null +++ b/src/main/java/agency/CarSpecifics.java @@ -0,0 +1,41 @@ +package agency; + +import util.TimeProvider; + +public class CarSpecifics implements VehiclesSpecifics { + private int numberOfSeats; + private int productionYear; + private static final int PRICE_PER_SEAT_FOR_OLD_CAR = 20; + private static final int PRICE_PER_SEAT_FOR_NEW_CAR = 40; + + public CarSpecifics(int numberOfSeats, int productionYear) { + if (numberOfSeats < 1) { + throw new IllegalArgumentException("Invalid number of seats: " + numberOfSeats); + } + this.numberOfSeats = numberOfSeats; + this.productionYear = productionYear; + } + + public int getNumberOfSeats() { + return numberOfSeats; + } + + public int getProductionYear() { + return productionYear; + } + + boolean isNew() { + int currentYear = TimeProvider.currentYearValue(); + return (currentYear - productionYear) <= 5; + } + + @Override + public double calculateDailyRentalPrice() { + return numberOfSeats * (isNew() ? PRICE_PER_SEAT_FOR_NEW_CAR : PRICE_PER_SEAT_FOR_OLD_CAR); + } + + @Override + public String getDetails() { + return (numberOfSeats > 1 ? numberOfSeats + " seats" : "1 seat"); + } +} \ No newline at end of file diff --git a/src/main/java/agency/Client.java b/src/main/java/agency/Client.java new file mode 100644 index 0000000000000000000000000000000000000000..11cb505da53832006096d3c34ca0d89c963d537f --- /dev/null +++ b/src/main/java/agency/Client.java @@ -0,0 +1,57 @@ +package agency; + +import java.util.Objects; + +public class Client { + private final int yearOfBirth; + private final String lastName; + private final String firstName; + + public Client(String firstName, String lastName, int yearOfBirth) { + if (yearOfBirth < 1900 || yearOfBirth > java.time.Year.now().getValue()) { + throw new IllegalArgumentException("Invalid year of birth: " + yearOfBirth); + } + if (lastName == null || lastName.isEmpty() || firstName == null || firstName.isEmpty()) { + throw new IllegalArgumentException("First name and last name cannot be null or empty."); + } + this.yearOfBirth = yearOfBirth; + this.lastName = lastName; + this.firstName = firstName; + } + + public int getYearOfBirth() { + return yearOfBirth; + } + + public String getLastName() { + return lastName; + } + + public String getFirstName() { + return firstName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Client client = (Client) o; + return yearOfBirth == client.yearOfBirth && + lastName.equals(client.lastName) && + firstName.equals(client.firstName); + } + + @Override + public int hashCode() { + return Objects.hash(yearOfBirth, lastName, firstName); + } + + @Override + public String toString() { + return "Client{" + + "yearOfBirth=" + yearOfBirth + + ", lastName='" + lastName + '\'' + + ", firstName='" + firstName + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/agency/IntersectionCriterion.java b/src/main/java/agency/IntersectionCriterion.java new file mode 100644 index 0000000000000000000000000000000000000000..573674c27c95c54c2a0ebe7bb026af2de0710a32 --- /dev/null +++ b/src/main/java/agency/IntersectionCriterion.java @@ -0,0 +1,25 @@ +package agency; + +import java.util.List; +import java.util.function.Predicate; + +public class IntersectionCriterion<T> implements Predicate<T> { + private final List<Predicate<T>> criteria; + + public IntersectionCriterion(List<Predicate<T>> criteria) { + if (criteria == null || criteria.isEmpty()) { + throw new IllegalArgumentException("The list of criteria cannot be null or empty."); + } + this.criteria = criteria; + } + + @Override + public boolean test(T t) { + for (Predicate<T> criterion : criteria) { + if (!criterion.test(t)) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/agency/MaxPriceCriterion.java b/src/main/java/agency/MaxPriceCriterion.java new file mode 100644 index 0000000000000000000000000000000000000000..e1d6f7351c19f8b4c086bea56411d296a126a77c --- /dev/null +++ b/src/main/java/agency/MaxPriceCriterion.java @@ -0,0 +1,26 @@ +import agency.Vehicle; + +import java.util.function.Predicate; + +public class MaxPriceCriterion implements Predicate<Vehicle> { + private final double maxPrice; + + public MaxPriceCriterion(double maxPrice) { + if (maxPrice < 0) { + throw new IllegalArgumentException("The maximum price must be non-negative."); + } + this.maxPrice = maxPrice; + } + + @Override + public boolean test(Vehicle vehicle) { + if (vehicle == null) { + return false; + } + return vehicle.dailyRentalPrice() <= maxPrice; + } + + public double getMaxPrice() { + return maxPrice; + } +} \ No newline at end of file diff --git a/src/main/java/agency/Motorbike.java b/src/main/java/agency/Motorbike.java new file mode 100644 index 0000000000000000000000000000000000000000..31a20a529516e7a7b62f51f555e65d8852b4fd65 --- /dev/null +++ b/src/main/java/agency/Motorbike.java @@ -0,0 +1,57 @@ +package agency; + +public class Motorbike implements Vehicle { + private String brand; + private String model; + private int productionYear; + private VehiclesSpecifics specifics; + + public Motorbike(String brand, String model, int productionYear, int cylinderCapacity) { + if (productionYear < 1900 || productionYear > util.TimeProvider.currentYearValue()) { + throw new IllegalArgumentException("Invalid production year: " + productionYear); + } + + this.brand = brand; + this.model = model; + this.productionYear = productionYear; + this.specifics = new MotorbikeSpecifics(cylinderCapacity); + } + + @Override + public String getBrand() { + return brand; + } + + @Override + public String getModel() { + return model; + } + + @Override + public int getProductionYear() { + return productionYear; + } + + @Override + public double dailyRentalPrice() { + return specifics.calculateDailyRentalPrice(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Motorbike motorbike = (Motorbike) o; + + return productionYear == motorbike.productionYear && + brand.equals(motorbike.brand) && + model.equals(motorbike.model); + } + + @Override + public String toString() { + return "Motorbike " + brand + " " + model + " " + productionYear + + " (" + specifics.getDetails() + ") : " + dailyRentalPrice() + "€"; + } +} \ No newline at end of file diff --git a/src/main/java/agency/MotorbikeSpecifics.java b/src/main/java/agency/MotorbikeSpecifics.java new file mode 100644 index 0000000000000000000000000000000000000000..4c4e4ba9d79777613cb16b3869e79178e248ca64 --- /dev/null +++ b/src/main/java/agency/MotorbikeSpecifics.java @@ -0,0 +1,24 @@ +package agency; + +public class MotorbikeSpecifics implements VehiclesSpecifics { + private int cylinderCapacity; + private static final int MINIMAL_CAPACITY = 50; + private static final double PRICE_PER_UNIT_OF_CYLINDER_CAPACITY = 0.25; + + public MotorbikeSpecifics(int cylinderCapacity) { + if (cylinderCapacity < MINIMAL_CAPACITY) { + throw new IllegalArgumentException("Cylinder capacity too low: " + cylinderCapacity + "cm³. Minimum is " + MINIMAL_CAPACITY + "cm³."); + } + this.cylinderCapacity = cylinderCapacity; + } + + @Override + public double calculateDailyRentalPrice() { + return cylinderCapacity * PRICE_PER_UNIT_OF_CYLINDER_CAPACITY; + } + + @Override + public String getDetails() { + return cylinderCapacity + "cm³"; + } +} \ No newline at end of file diff --git a/src/main/java/agency/RentalAgency.java b/src/main/java/agency/RentalAgency.java new file mode 100644 index 0000000000000000000000000000000000000000..21d1d8d02a5932490f7c4d642c197780c3991667 --- /dev/null +++ b/src/main/java/agency/RentalAgency.java @@ -0,0 +1,88 @@ +package agency; + +import java.util.*; +import java.util.function.Predicate; + +public class RentalAgency { + private final List<Vehicle> vehicles; + private final Map<Client, Vehicle> rentedVehicles; + + public RentalAgency() { + this.vehicles = new ArrayList<>(); + this.rentedVehicles = new HashMap<>(); + } + + public RentalAgency(List<Vehicle> vehicles) { + this.vehicles = new ArrayList<>(vehicles); + this.rentedVehicles = new HashMap<>(); + } + + public boolean contains(Vehicle vehicle) { + return vehicles.contains(vehicle); + } + + public boolean add(Vehicle vehicle) { + if (!vehicles.contains(vehicle)) { + vehicles.add(vehicle); + return true; + } + return false; + } + + public void remove(Vehicle vehicle) { + if (!vehicles.remove(vehicle)) { + throw new UnknownVehicleException(vehicle); + } + } + + public List<Vehicle> getVehicles() { + return new ArrayList<>(vehicles); + } + + public List<Vehicle> select(Predicate<Vehicle> criterion) { + return vehicles.stream() + .filter(criterion) + .toList(); + } + + public void printSelectedVehicles(Predicate<Vehicle> criterion) { + vehicles.stream() + .filter(criterion) + .forEach(System.out::println); + } + + + public boolean vehicleIsRented(Vehicle vehicle) { + return rentedVehicles.containsValue(vehicle); + } + + + public boolean aVehicleIsRentedBy(Client client) { + return rentedVehicles.containsKey(client); + } + + + public double rentVehicle(Client client, Vehicle vehicle) throws UnknownVehicleException, IllegalStateException { + if (!vehicles.contains(vehicle)) { + throw new UnknownVehicleException(vehicle); + } + if (vehicleIsRented(vehicle)) { + throw new IllegalStateException("The vehicle is already rented."); + } + if (aVehicleIsRentedBy(client)) { + throw new IllegalStateException("The client is already renting another vehicle."); + } + + rentedVehicles.put(client, vehicle); + return vehicle.dailyRentalPrice(); + } + + + public void returnVehicle(Client client) { + rentedVehicles.remove(client); + } + + public Collection<Vehicle> allRentedVehicles() { + return rentedVehicles.values(); + } +} \ No newline at end of file diff --git a/src/main/java/agency/UnknownVehicleException.java b/src/main/java/agency/UnknownVehicleException.java new file mode 100644 index 0000000000000000000000000000000000000000..046d0e930cc396484c59bcd919477b5e70e65be6 --- /dev/null +++ b/src/main/java/agency/UnknownVehicleException.java @@ -0,0 +1,18 @@ +package agency; + +public class UnknownVehicleException extends RuntimeException { + private final Vehicle vehicle; + + public UnknownVehicleException(Vehicle vehicle) { + this.vehicle = vehicle; + } + + @Override + public String getMessage() { + return "The vehicle does not exist in the agency!!: " + vehicle.toString(); + } + + public Vehicle getVehicle() { + return vehicle; + } +} \ No newline at end of file diff --git a/src/main/java/agency/Vehicle.java b/src/main/java/agency/Vehicle.java new file mode 100644 index 0000000000000000000000000000000000000000..11f9526ce24af2b748189b4f9b559c1b2f6d7b0d --- /dev/null +++ b/src/main/java/agency/Vehicle.java @@ -0,0 +1,18 @@ +package agency; + +public interface Vehicle { + + String getBrand(); + + String getModel(); + + int getProductionYear(); + + double dailyRentalPrice(); + + @Override + boolean equals(Object o); + + @Override + String toString(); +} \ No newline at end of file diff --git a/src/main/java/agency/VehiclesSpecifics.java b/src/main/java/agency/VehiclesSpecifics.java new file mode 100644 index 0000000000000000000000000000000000000000..a3c05928f2a7a599ffa96c8fa7fdf648e6ac7dca --- /dev/null +++ b/src/main/java/agency/VehiclesSpecifics.java @@ -0,0 +1,6 @@ +package agency; + +public interface VehiclesSpecifics { + double calculateDailyRentalPrice(); + String getDetails(); +} \ No newline at end of file diff --git a/src/test/java/agency/TestCar.java b/src/test/java/agency/TestCar.java index 78d80931f50a795542ecafa7b9012ca379ad7ff2..522d9d36a0328c33ae0bd33e644fef8742182513 100644 --- a/src/test/java/agency/TestCar.java +++ b/src/test/java/agency/TestCar.java @@ -12,8 +12,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException class TestCar { - // TODO : décommenter le code ci-dessous pour tester la tâche 2. - /* Car catalinaCar = new Car(apple, catalina, catalinaYear, catalinaNumberOfSeats); Car windows95Car = new Car(microsoft, windows95, windows95Year, windows95NumberOfSeats); @@ -120,5 +118,4 @@ class TestCar { String expectedStringWin = "Car Microsoft Windows95 1995 (1 seat) : 20.0€"; assertThat(windows95Car).returns(expectedStringWin, Objects::toString); } - */ } diff --git a/src/test/java/agency/TestRentalAgency.java b/src/test/java/agency/TestRentalAgency.java index 7ecab66602ea3ac39d1bbc906badc3c943dbabf9..3d998c10056330427b785ba6c4cf772bae8b2e26 100644 --- a/src/test/java/agency/TestRentalAgency.java +++ b/src/test/java/agency/TestRentalAgency.java @@ -8,8 +8,6 @@ import static org.assertj.core.api.Assertions.*; class TestRentalAgency extends agency.TestCar { - // TODO : décommenter le code ci-dessous pour tester la tâche 2. - /* private RentalAgency rentalAgency; @BeforeEach @@ -40,21 +38,15 @@ class TestRentalAgency extends agency.TestCar { assertThat(rentalAgency.getVehicles()).doesNotContain(catalinaCar); } - */ - // TODO : décommenter le code ci-dessous pour tester la méthode select. - - /* @Test void testRemovingNonExistingVehicle(){ assertThatThrownBy(() -> rentalAgency.remove(catalinaCar)) .isInstanceOf(UnknownVehicleException.class) .hasMessageContaining(catalinaCar.toString()); } - */ - // TODO : décommenter le code ci-dessous pour tester la tâche 6. - /* + @Test void testSelect(){ rentalAgency.add(catalinaCar); @@ -66,7 +58,6 @@ class TestRentalAgency extends agency.TestCar { } - private Client arnaud = new Client("Arnaud", "Labourel", 1981); private Client paul = new Client("Paul", "Calcul", 2018); @@ -102,5 +93,4 @@ class TestRentalAgency extends agency.TestCar { .isInstanceOf(UnknownVehicleException.class) .hasMessageContaining(catalinaCar.toString()); } - */ }