aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol')
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ArchiveOperationController.java213
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateCarController.java231
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateNewEmployeeController.java174
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateOperationController.java371
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CustomListItemController.java24
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/DetailArchiveOperationController.java77
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListController.java133
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListItemController.java87
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/FilterEmployeesController.java65
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/Helper.java34
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ManageEmployeesController.java120
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationDetailsController.java169
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationInArchiveController.java65
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/RegistrationWindowController.java279
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/VehiclePaneController.java118
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDAO.java44
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDatabaseDAO.java144
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDAO.java48
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDatabaseDAO.java221
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDAO.java28
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDatabaseDAO.java130
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDAO.java54
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDatabaseDAO.java211
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Employee.java51
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/EmployeeValidator.java23
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Operation.java70
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Registration.java34
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/RegistrationValidator.java174
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Vehicle.java73
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeService.java46
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeServiceImpl.java59
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationService.java70
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java286
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationService.java32
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationServiceImpl.java52
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleService.java49
-rw-r--r--src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleServiceImpl.java116
37 files changed, 4175 insertions, 0 deletions
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ArchiveOperationController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ArchiveOperationController.java
new file mode 100644
index 0000000..6e0c89d
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ArchiveOperationController.java
@@ -0,0 +1,213 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.OperationService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.FlowPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class ArchiveOperationController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ArchiveOperationController.class);
+
+ @FXML private ImageView imvVehicleDetail;
+ @FXML private Label lblStatus;
+ @FXML private AnchorPane apMainDetails;
+ @FXML private Label lblOperations;
+ @FXML private Label lblCompleted;
+ @FXML private Label lblCancelled;
+ @FXML private AnchorPane backApMain;
+ @FXML private AnchorPane backApDetails;
+ @FXML private AnchorPane archiveOperationAP;
+ @FXML private AnchorPane apDetails;
+ @FXML private Label lblCodeHeader;
+ @FXML private Label lblOpCode;
+ @FXML private Label lblVehicles;
+ @FXML private Label lblDate;
+ @FXML private Label lblAddress;
+ @FXML private FlowPane fpVehicles;
+ private final OperationService operationService;
+ @FXML private FlowPane archiveOperationFlowPane;
+ private final CreateOperationController createOperationController;
+ private Set<Operation> list = new HashSet<>();
+ private final SpringFXMLLoader fxmlLoader;
+
+ public ArchiveOperationController(
+ OperationService operationService,
+ CreateOperationController createOperationController,
+ SpringFXMLLoader fxmlLoader) {
+ this.operationService = operationService;
+ this.createOperationController = createOperationController;
+ this.fxmlLoader = fxmlLoader;
+ }
+
+ @FXML
+ private void initialize() {
+ update();
+ }
+
+ public void update() {
+ archiveOperationFlowPane.getChildren().clear();
+ list.clear();
+ try {
+ list.addAll(operationService.list(EnumSet.of(Status.CANCELLED, Status.COMPLETED)));
+ long cancelledAmount = 0;
+ long completedAmount = 0;
+ for (Operation operation : list) {
+ if (operation.status() == Status.CANCELLED) cancelledAmount++;
+ else completedAmount++;
+ }
+ lblCancelled.setText("storniert: " + cancelledAmount);
+ lblCompleted.setText("abgeschlossen: " + completedAmount);
+ lblOperations.setText("Einsätze: " + list.size());
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in update().", e);
+ showServiceExceptionAlertAndWait("Die Einsätze konnten nicht geladen werden!");
+ ;
+ }
+ setFlowPane();
+ }
+
+ private void setFlowPane() {
+ try {
+ archiveOperationFlowPane.getChildren().clear();
+ for (Operation operation : sortSet(list)) {
+ OperationInArchiveController opInAController =
+ OperationInArchiveController.create();
+ opInAController.set(operation);
+ opInAController
+ .getRoot()
+ .setOnMouseClicked(
+ event -> {
+ detailOperation = operation;
+ backApMain.setVisible(false);
+ apMainDetails.setVisible(false);
+ backApDetails.setVisible(true);
+ setOperation();
+ setDetailsVisible(true);
+ imvVehicleDetail.setImage(new Image("/images/Vehicle.png"));
+ });
+ archiveOperationFlowPane.getChildren().add(opInAController.getRoot());
+ }
+ } catch (IOException e) {
+ LOG.error("IOException in setFlowPane(). ", e);
+ showServiceExceptionAlertAndWait("Die Elemente konnten nicht geladen werden!");
+ }
+ }
+
+ private Operation detailOperation;
+
+ private List<Operation> sortSet(Set<Operation> operationsSet) {
+ Operation[] array = operationsSet.toArray(new Operation[operationsSet.size()]);
+ for (int i = array.length - 1; i > 0; i--) {
+ for (int j = 0; j < i; j++) {
+ LocalDateTime first =
+ LocalDateTime.ofInstant(
+ Objects.requireNonNull(array[j].created()), ZoneOffset.UTC);
+ LocalDateTime second =
+ LocalDateTime.ofInstant(
+ Objects.requireNonNull(array[j + 1].created()), ZoneOffset.UTC);
+ if (first.isBefore(second)) {
+ Operation help = array[j];
+ array[j] = array[j + 1];
+ array[j + 1] = help;
+ }
+ }
+ }
+ return Arrays.asList(array);
+ }
+
+ private void setOperation() {
+ lblCodeHeader.setText(detailOperation.opCode());
+ if (detailOperation.created() != null) {
+ LocalDateTime dateTime =
+ LocalDateTime.ofInstant(
+ Objects.requireNonNull(detailOperation.created()), ZoneOffset.UTC);
+ lblDate.setText(
+ "am "
+ + dateTime.getDayOfMonth()
+ + "."
+ + dateTime.getMonth().getValue()
+ + "."
+ + dateTime.getYear());
+ } else {
+ lblDate.setText("---");
+ }
+ lblStatus.setText(
+ "Status: "
+ + (detailOperation.status() == Status.CANCELLED
+ ? "storniert"
+ : "abgeschlossen"));
+ lblOpCode.setText(detailOperation.opCode());
+ Collection<String> elements =
+ detailOperation.vehicles().stream().map(Vehicle::name).collect(Collectors.toList());
+ String result = String.join(", ", elements);
+
+ lblVehicles.setText(result);
+ lblAddress.setText(detailOperation.destination());
+
+ fpVehicles.getChildren().clear();
+ try {
+ for (Vehicle vehicle : detailOperation.vehicles()) {
+ DetailArchiveOperationController controller = null;
+
+ controller = DetailArchiveOperationController.create(fxmlLoader);
+
+ controller.set(vehicle);
+ fpVehicles.getChildren().add(controller.getRoot());
+ }
+ } catch (IOException e) {
+ LOG.error("IOException in setOperation(). ", e);
+ showServiceExceptionAlertAndWait("Die Element konnte nicht geladen werden!");
+ }
+ }
+
+ private void setDetailsVisible(boolean b) {
+ apDetails.setVisible(b);
+ }
+
+ public void backClicked() {
+ LOG.debug("Hyperlink \"Zurück\" in archive detail view clicked.");
+ fpVehicles.getChildren().clear();
+ setDetailsVisible(false);
+ backApDetails.setVisible(false);
+ apMainDetails.setVisible(true);
+ backApMain.setVisible(true);
+ }
+
+ public void backToMain() {
+ LOG.debug("Hyperlink \"Zurück\" in archive main view clicked.");
+ this.setVisible(false);
+ createOperationController.setVisible(true);
+ }
+
+ void setVisible(boolean b) {
+ archiveOperationAP.setVisible(b);
+ backApMain.setVisible(b);
+ apMainDetails.setVisible(b);
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateCarController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateCarController.java
new file mode 100644
index 0000000..aa76535
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateCarController.java
@@ -0,0 +1,231 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showSuccessAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showValidationErrorAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.ConstructionType;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.VehicleType;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.VehicleService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javafx.collections.FXCollections;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.FlowPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class CreateCarController {
+
+ @FXML private AnchorPane createCarAP;
+ @FXML private ChoiceBox<String> cmbCtype;
+ @FXML private ChoiceBox<String> cmbTyp;
+
+ @FXML private Button btnCreate;
+ @FXML private CheckBox cbxNEF;
+ @FXML private FlowPane fpVehicleList;
+ private final CreateOperationController createOperationController;
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateCarController.class);
+ private final VehicleService vehicleService;
+ private boolean update = false;
+ private long vid = -1;
+
+ private Vehicle chooseVehicle;
+
+ public CreateCarController(
+ CreateOperationController createOperationController, VehicleService vehicleService) {
+ this.createOperationController = createOperationController;
+ this.vehicleService = vehicleService;
+ }
+
+ @FXML
+ private void initialize() {
+ fpVehicleList.setHgap(5);
+ fpVehicleList.setVgap(5);
+
+ cmbCtype.setItems(
+ FXCollections.observableArrayList(
+ Stream.of(
+ ConstructionType.NORMAL,
+ ConstructionType.MITTELHOCHDACH,
+ ConstructionType.HOCHDACH)
+ .map(Enum::toString)
+ .collect(Collectors.toList())));
+ cmbCtype.setValue(ConstructionType.NORMAL.toString());
+ cmbTyp.setItems(
+ FXCollections.observableArrayList(
+ Stream.of(
+ VehicleType.BKTW,
+ VehicleType.KTW_B,
+ VehicleType.KTW,
+ VehicleType.RTW,
+ VehicleType.NEF,
+ VehicleType.NAH)
+ .map(Enum::toString)
+ .collect(Collectors.toList())));
+ cmbTyp.setValue(VehicleType.BKTW.toString());
+
+ updateVehiclePane();
+ }
+
+ @FXML
+ private void createCar(ActionEvent actionEvent) {
+
+ if (!update) {
+ LOG.debug("Button \"Erstellen\" clicked.");
+ Vehicle vehicle =
+ Vehicle.builder()
+ .constructionType(parseConstructionType())
+ .type(parseType())
+ .name("")
+ .status(Status.ABGEMELDET)
+ .hasNef(cbxNEF.isSelected())
+ .build();
+ try {
+ vehicleService.add(vehicle);
+ setToStart();
+ } catch (InvalidVehicleException e) {
+ LOG.debug("Validation of Vehicle failed");
+ showValidationErrorAlertAndWait(e.getMessage());
+ setToStart();
+ return;
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in createCar(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Ein Fehler beim Erstellen des Fahrzeuges ist aufgetreten.");
+ setToStart();
+ return;
+ }
+ showSuccessAlertAndWait("Auto wurde erfolgreich angelegt.");
+ } else {
+ LOG.debug("Button \"Speichern\" clicked.");
+ try {
+ Vehicle vehicle =
+ Vehicle.builder()
+ .id(vid)
+ .constructionType(parseConstructionType())
+ .type(parseType())
+ .name("")
+ .status(Status.ABGEMELDET)
+ .hasNef(cbxNEF.isSelected())
+ .build();
+ vehicleService.update(vehicle);
+ setToStart();
+ chooseVehicle = null;
+ } catch (InvalidVehicleException e) {
+ LOG.debug("Validation of Vehicle failed");
+ showValidationErrorAlertAndWait(e.getMessage());
+ setToStart();
+ return;
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in createCar(). ", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ setToStart();
+ return;
+ }
+ showSuccessAlertAndWait("Auto wurde erfolgreich bearbeitet.");
+ }
+
+ updateVehiclePane();
+ }
+
+ private ConstructionType parseConstructionType() {
+ if (cmbCtype.getSelectionModel().getSelectedItem() == null) {
+ return ConstructionType.NORMAL;
+ }
+ return ConstructionType.valueOf(cmbCtype.getSelectionModel().getSelectedItem());
+ }
+
+ private VehicleType parseType() {
+ if (cmbTyp.getSelectionModel().getSelectedItem() == null) {
+ return VehicleType.BKTW;
+ }
+ return VehicleType.valueOf(cmbTyp.getSelectionModel().getSelectedItem());
+ }
+
+ private void setToStart() {
+ btnCreate.setText("Erstellen");
+ cbxNEF.setSelected(false);
+ cmbTyp.setValue(VehicleType.BKTW.name());
+ cmbCtype.setValue(ConstructionType.NORMAL.name());
+ update = false;
+ }
+
+ private void updateVehicle(Vehicle vehicle) {
+
+ cmbCtype.setValue(vehicle.constructionType().name());
+ cmbTyp.setValue(vehicle.type().name());
+ cbxNEF.setSelected(vehicle.hasNef());
+ btnCreate.setText("Speichern");
+ vid = vehicle.id();
+ update = true;
+ chooseVehicle = vehicle;
+ }
+
+ public void setVisible(boolean b) {
+ createCarAP.setVisible(b);
+ }
+
+ @FXML
+ private void backToMain() {
+ LOG.debug("Hyperlink \"zurück\" clicked.");
+ this.setVisible(false);
+ createOperationController.setVisible(true);
+ }
+
+ private void updateVehiclePane() {
+ try {
+ fpVehicleList.getChildren().clear();
+
+ Set<Vehicle> vehicles;
+
+ vehicles = vehicleService.list(EnumSet.of(Status.ABGEMELDET));
+
+ for (Vehicle vehicle : vehicles) {
+ VehiclePaneController controller = VehiclePaneController.createVehiclePane();
+
+ controller.setData(vehicle, false, false);
+ controller
+ .getRootElement()
+ .setOnMouseClicked(
+ event -> {
+ if (event.getButton().equals(MouseButton.PRIMARY)) {
+ if (chooseVehicle == null || vehicle == chooseVehicle) {
+ if (update == false) {
+ chooseVehicle = vehicle;
+ updateVehicle(vehicle);
+ controller.setSelected(true);
+ } else {
+ setToStart();
+ controller.setSelected(false);
+
+ chooseVehicle = null;
+ }
+ }
+ }
+ });
+
+ fpVehicleList.getChildren().add(controller.getRootElement());
+ }
+ } catch (ServiceException | IOException e) {
+ LOG.error("Exception in updateVehiclePane(). ", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateNewEmployeeController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateNewEmployeeController.java
new file mode 100644
index 0000000..433bfa6
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateNewEmployeeController.java
@@ -0,0 +1,174 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showSuccessAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showValidationErrorAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee.EducationLevel;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.EmployeeService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader.FXMLWrapper;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Controller;
+
+@Controller
+@Scope("prototype")
+public class CreateNewEmployeeController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateNewEmployeeController.class);
+ private final EmployeeService employeeService;
+
+ @FXML private Label lblHeader;
+ @FXML private CheckBox inputIsDriver;
+ @FXML private CheckBox inputIsPilot;
+ @FXML private TextField inputName;
+ @FXML private ChoiceBox<String> inputQualification;
+ @FXML private Button btnCreate;
+
+ private Node rootElement;
+ private Employee employee;
+ private boolean isEdit;
+
+ private Runnable consumerCancelClicked;
+ private Runnable consumerCreateClicked;
+
+ public CreateNewEmployeeController(EmployeeService employeeService) {
+ this.employeeService = employeeService;
+ }
+
+ @FXML
+ private void initialize() {
+ inputQualification.setItems(
+ FXCollections.observableArrayList(
+ Stream.of(
+ EducationLevel.RS,
+ EducationLevel.NFS,
+ EducationLevel.NKV,
+ EducationLevel.NKA,
+ EducationLevel.NKI,
+ EducationLevel.NA)
+ .map(Enum::toString)
+ .collect(Collectors.toList())));
+
+ inputQualification.setValue(EducationLevel.RS.toString());
+ employee =
+ Employee.builder()
+ .name("")
+ .educationLevel(EducationLevel.RS)
+ .isDriver(false)
+ .isPilot(false)
+ .birthday(LocalDate.MIN)
+ .build();
+ }
+
+ @FXML
+ private void onCancelClicked() {
+ LOG.debug("Hyperlink \"abbrechen\" clicked.");
+ if (consumerCancelClicked != null) {
+ consumerCancelClicked.run();
+ }
+ }
+
+ @FXML
+ private void onCreateClicked() {
+ LOG.debug("Button {} clicked.", btnCreate.getText());
+
+ employee =
+ employee.toBuilder()
+ .name(inputName.getText())
+ .educationLevel(parseEducationLevel())
+ .birthday(LocalDate.MIN) // TODO: change UI to include birthday field
+ .isDriver(inputIsDriver.isSelected())
+ .isPilot(inputIsPilot.isSelected())
+ .build();
+
+ try {
+ if (isEdit) {
+ employeeService.update(employee);
+ } else {
+ employeeService.add(employee);
+ }
+ } catch (InvalidEmployeeException e) {
+ LOG.debug("Validation for Employee failed");
+ showValidationErrorAlertAndWait(e.getMessage());
+ return;
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in onCreateClicked(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Der Eintrag konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.");
+ return;
+ }
+
+ showSuccessAlertAndWait(
+ "Der/die MitarbeiterIn wurde erfolgreich angelegt und gespeichert!");
+
+ if (consumerCreateClicked != null) {
+ consumerCreateClicked.run();
+ }
+ }
+
+ private EducationLevel parseEducationLevel() {
+ if (inputQualification.getSelectionModel().getSelectedItem() == null) {
+ return EducationLevel.RS;
+ }
+ return EducationLevel.valueOf(inputQualification.getSelectionModel().getSelectedItem());
+ }
+
+ private void setData(Employee employee) {
+ isEdit = true;
+ this.employee = employee;
+ inputName.setText(employee.name());
+ inputQualification.setValue(employee.educationLevel().name());
+ inputIsDriver.setSelected(employee.isDriver());
+ inputIsPilot.setSelected(employee.isPilot());
+ lblHeader.setText("Person bearbeiten");
+ btnCreate.setText("Speichern");
+ }
+
+ public static CreateNewEmployeeController createCreateNewEmployeeController(
+ SpringFXMLLoader fxmlLoader, Employee employee) throws IOException {
+ CreateNewEmployeeController controller = createCreateNewEmployeeController(fxmlLoader);
+ controller.setData(employee);
+ return controller;
+ }
+
+ public static CreateNewEmployeeController createCreateNewEmployeeController(
+ SpringFXMLLoader fxmlLoader) throws IOException {
+ FXMLWrapper<Object, CreateNewEmployeeController> wrapper =
+ fxmlLoader.loadAndWrap(
+ "/fxml/createNewEmployee.fxml", CreateNewEmployeeController.class);
+ Node root = (Node) wrapper.getLoadedObject();
+ CreateNewEmployeeController controller = wrapper.getController();
+ controller.rootElement = root;
+ return controller;
+ }
+
+ public Node getRootElement() {
+ return rootElement;
+ }
+
+ public void setConsumerCancelClicked(Runnable consumerCancelClicked) {
+ this.consumerCancelClicked = consumerCancelClicked;
+ }
+
+ public void setConsumerCreateClicked(Runnable consumerCreateClicked) {
+ this.consumerCreateClicked = consumerCreateClicked;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateOperationController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateOperationController.java
new file mode 100644
index 0000000..b237265
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CreateOperationController.java
@@ -0,0 +1,371 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showSuccessAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showValidationErrorAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.OperationService;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.VehicleService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.GridPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class CreateOperationController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateOperationController.class);
+
+ public AnchorPane apCreateOperation;
+ @FXML private GridPane grdWindowContainer;
+ @FXML private TextField txtCode;
+ @FXML private TextField txtAddress;
+ @FXML private TextField txtNote;
+ @FXML private Button btnCreateOperation;
+ @FXML private ListView<Vehicle> lvVehicles;
+ @FXML private ListView<Operation> lvActiveOperations;
+ @FXML private Label lblChosenVehicles;
+ @FXML private AnchorPane apInvisible;
+ @FXML private OperationDetailsController operationDetailsController;
+ @FXML private ManageEmployeesController manageEmployeesController;
+ @FXML private CreateCarController createCarController;
+ @FXML private RegistrationWindowController registrationWindowController;
+ @FXML private ArchiveOperationController archiveOperationController;
+ @FXML private FlowPane fpVehicles;
+
+ private LinkedList<Vehicle> chosenVehicles = new LinkedList<>();
+
+ private final OperationService operationService;
+ private final VehicleService vehicleService;
+
+ public CreateOperationController(
+ OperationService operationService, VehicleService vehicleService) {
+ this.operationService = operationService;
+ this.vehicleService = vehicleService;
+ }
+
+ @FXML
+ private void initialize() {
+
+ lblChosenVehicles.setText("keine ausgewählt");
+ lvActiveOperations.setCellFactory(param -> generateOpCodeListItem());
+
+ lvActiveOperations.setOnMouseClicked(
+ event -> {
+ if (event.getClickCount() == 2) {
+ if (lvActiveOperations.getSelectionModel().getSelectedItem() == null) {
+ return;
+ }
+ openDetailsWindow(lvActiveOperations.getSelectionModel().getSelectedItem());
+ }
+ });
+
+ setVisible(true);
+ createCarController.setVisible(false);
+ registrationWindowController.setVisible(false);
+ }
+
+ public void updateList() {
+ try {
+ fpVehicles.getChildren().clear();
+
+ // TODO: this should probably be handled differently
+ Set<Vehicle> vehicles;
+ if (txtCode.getText().isEmpty()) {
+ vehicles =
+ vehicleService.list(
+ EnumSet.complementOf(EnumSet.of(Vehicle.Status.ABGEMELDET)));
+ } else {
+ vehicles = operationService.rankVehicles(txtCode.getText());
+ }
+
+ for (Vehicle vehicle : vehicles) {
+ VehiclePaneController controller = VehiclePaneController.createVehiclePane();
+
+ controller.setData(vehicle, true, false);
+ controller
+ .getRootElement()
+ .setOnMouseClicked(
+ event -> {
+ if (event.getButton().equals(MouseButton.SECONDARY)) {
+ createContextMenu(vehicle, vehicleService)
+ .show(
+ controller.getRootElement(),
+ event.getScreenX(),
+ event.getScreenY());
+ } else {
+ if (chosenVehicles.contains(vehicle)) {
+ chosenVehicles.remove(vehicle);
+ controller.setSelected(false);
+ } else {
+ chosenVehicles.add(vehicle);
+ controller.setSelected(true);
+ }
+
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < chosenVehicles.size(); i++) {
+ if (i == chosenVehicles.size() - 1) {
+ result.append(chosenVehicles.get(i).name());
+ } else {
+ result.append(chosenVehicles.get(i).name())
+ .append(", ");
+ }
+ }
+ if (result.toString().equals("")) {
+ lblChosenVehicles.setText("keine ausgewählt");
+ } else {
+ lblChosenVehicles.setText(result.toString());
+ }
+ }
+ });
+
+ if (chosenVehicles.stream().anyMatch(v -> v.id() == vehicle.id()))
+ controller.setSelected(true);
+
+ fpVehicles.getChildren().add(controller.getRootElement());
+ }
+ } catch (ServiceException | IOException e) {
+ LOG.error("Exception in updateList(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Erstellen des Ranking ist ein Fehler aufgetreten.");
+ } catch (InvalidOperationException e) {
+ LOG.debug("Validation error in updateList(). ", e);
+ showValidationErrorAlertAndWait(e.getMessage());
+ }
+ try {
+ lvActiveOperations.setItems(
+ FXCollections.observableArrayList(
+ operationService.list(EnumSet.of(Status.ACTIVE))));
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in updateList(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Holen der aktiven Einsätze ist ein Fehler aufgetreten");
+ }
+ }
+
+ private ContextMenu createContextMenu(Vehicle data, VehicleService vehicleService) {
+ ContextMenu menu = new ContextMenu();
+
+ for (Vehicle.Status status : Vehicle.Status.values()) {
+ if (status == Vehicle.Status.ABGEMELDET) {
+ continue;
+ }
+
+ MenuItem mi = new MenuItem(status.name());
+
+ if (status == Vehicle.Status.FREI_FUNK || status == Vehicle.Status.FREI_WACHE) {
+ mi.getStyleClass().add("mi-free");
+ } else {
+ mi.getStyleClass().add("mi-other");
+ }
+
+ mi.setOnAction(
+ event -> {
+ try {
+ vehicleService.update(data.toBuilder().status(status).build());
+ this.updateList();
+ } catch (InvalidVehicleException e) {
+ LOG.debug(
+ "Validation error in createContextMenu(). (mi.setOnAction) ",
+ e);
+ showValidationErrorAlertAndWait(e.getMessage());
+ } catch (ServiceException e) {
+ LOG.error("Exception in createContextMenu(). (mi.setOnAction) ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Aktualisieren der Fahrzeuge ist ein Fehler aufgetreten.");
+ }
+ });
+
+ menu.getItems().add(mi);
+ }
+
+ MenuItem abmelden = new MenuItem("abmelden");
+
+ abmelden.setOnAction(
+ event -> {
+ try {
+ List<Registration> registrations = data.registrations();
+ assert registrations
+ != null; // Otherwise the element shouldn't be in the list.
+
+ List<Registration> newRegistrations = new ArrayList<>();
+ Instant now = Instant.now();
+
+ for (Registration registration : registrations) {
+ if (registration.start().isBefore(now)
+ && registration.end().isAfter(now)) {
+ newRegistrations.add(
+ registration
+ .toBuilder()
+ .end(Instant.now().minus(1, ChronoUnit.SECONDS))
+ .build());
+ } else newRegistrations.add(registration);
+ }
+
+ vehicleService.update(
+ data.toBuilder()
+ .registrations(newRegistrations)
+ .status(Vehicle.Status.ABGEMELDET)
+ .build());
+
+ this.updateList();
+ } catch (InvalidVehicleException e) {
+ LOG.debug(
+ "Validation error in createContextMenu(). (abmelden.setOnAction) ",
+ e);
+ showValidationErrorAlertAndWait(e.getMessage());
+ } catch (ServiceException e) {
+ LOG.error("Exception in createContextMenu(). (abmelden.setOnAction) ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Aktualisieren der Fahrzeuge ist ein Fehler aufgetreten.");
+ }
+ });
+
+ menu.getItems().add(abmelden);
+ return menu;
+ }
+
+ @FXML
+ protected void createOperationClicked() {
+ LOG.debug("Button \"Erstellen\" clicked.");
+ Vehicle[] vehicles = new Vehicle[chosenVehicles.size()];
+ for (int i = 0; i < chosenVehicles.size(); i++) {
+ vehicles[i] = chosenVehicles.get(i);
+ }
+ Operation operation =
+ Operation.builder()
+ .additionalInfo(txtNote.getText())
+ .destination(txtAddress.getText())
+ .opCode(txtCode.getText())
+ .status(Status.ACTIVE)
+ .vehicles(Set.of(vehicles))
+ .build();
+ try {
+ operationService.add(operation);
+ } catch (ServiceException e) {
+ LOG.error("Exception in createOperationClicked(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Erstellen des Einsatzes ist ein Fehler aufgetreten.");
+ return;
+ } catch (InvalidOperationException e) {
+ LOG.debug("Validation error in createOperationClicked(). ", e);
+ showValidationErrorAlertAndWait(e.getMessage());
+ return;
+ }
+ showSuccessAlertAndWait("Der Einsatz wurde erfolgreich gespeichert.");
+ updateList();
+ lblChosenVehicles.setText("keine ausgewählt");
+ txtAddress.setText("");
+ txtCode.setText("");
+ txtNote.setText("");
+ chosenVehicles = new LinkedList<>();
+ }
+
+ @FXML
+ private void onRegistrationLinkClicked() {
+ LOG.debug("Hyperlink \"Anmeldungen\" clicked.");
+ openRegistrationWindow();
+ }
+
+ @FXML
+ private void onEmployeeLinkClicked() {
+ LOG.debug("Hyperlink \"Personen\" clicked.");
+ openCreateNewEmployeeWindow();
+ }
+
+ @FXML
+ private void onVehicleLinkClicked() {
+ LOG.debug("Hyperlink \"Fahrzeuge\" clicked.");
+ openCreateCarWindow();
+ }
+
+ @FXML
+ private void onArchivLinkClicked() {
+ LOG.debug("Hyperlink \"Archiv\" clicked.");
+ archiveOperationController.update();
+ openArchivWindow();
+ }
+
+ private void openArchivWindow() {
+ archiveOperationController.setVisible(true);
+ this.setVisible(false);
+ }
+
+ void setVisible(boolean b) {
+ apInvisible.setVisible(!b);
+ grdWindowContainer.setVisible(!b);
+
+ updateList();
+ }
+
+ private void openDetailsWindow(Operation operation) {
+ operationDetailsController.initOperation(operation);
+ this.setVisible(false);
+ }
+
+ private void openCreateNewEmployeeWindow() {
+ this.setVisible(false);
+ manageEmployeesController.setVisible(true);
+ }
+
+ private void openCreateCarWindow() {
+ this.setVisible(false);
+ createCarController.setVisible(true);
+ }
+
+ private void openRegistrationWindow() {
+ this.setVisible(false);
+ registrationWindowController.setVisible(true);
+ }
+
+ @FXML
+ private void onOperationCodeChanged(KeyEvent keyEvent) {
+ if (keyEvent.getCode() == KeyCode.ENTER) {
+ updateList();
+ }
+ }
+
+ static ListCell<Operation> generateOpCodeListItem() {
+ return new ListCell<>() {
+ @Override
+ protected void updateItem(Operation item, boolean empty) {
+ super.updateItem(item, empty);
+
+ if (empty || item == null || item.opCode() == null) {
+ setText(null);
+ } else {
+ setText(item.opCode());
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CustomListItemController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CustomListItemController.java
new file mode 100644
index 0000000..ced0c10
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/CustomListItemController.java
@@ -0,0 +1,24 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import javafx.scene.Node;
+
+public abstract class CustomListItemController {
+
+ protected Node rootElement;
+
+ public Node getRootElement() {
+ return rootElement;
+ }
+
+ public void setSelected(boolean selected) {
+ rootElement.getStyleClass().clear();
+
+ if (selected) {
+ rootElement.getStyleClass().add("bg-yellow");
+ rootElement.getStyleClass().add("shadowed");
+ } else {
+ rootElement.getStyleClass().add("bg-white");
+ rootElement.getStyleClass().add("shadowed");
+ }
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/DetailArchiveOperationController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/DetailArchiveOperationController.java
new file mode 100644
index 0000000..32630a5
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/DetailArchiveOperationController.java
@@ -0,0 +1,77 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader.FXMLWrapper;
+import java.io.IOException;
+import java.util.Objects;
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.layout.VBox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class DetailArchiveOperationController {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(DetailArchiveOperationController.class);
+
+ @FXML private VBox vBoxVehicle;
+ @FXML private VBox vBoxPeople;
+ private final SpringFXMLLoader fxmlLoader;
+
+ public DetailArchiveOperationController(SpringFXMLLoader fxmlLoader) {
+ this.fxmlLoader = fxmlLoader;
+ }
+
+ static DetailArchiveOperationController create(SpringFXMLLoader fxmlLoader) throws IOException {
+ FXMLWrapper<Object, DetailArchiveOperationController> wrapper =
+ fxmlLoader.loadAndWrap(
+ "/fxml/DetailArchiveOperation.fxml",
+ DetailArchiveOperationController.class);
+
+ Node root = (Node) wrapper.getLoadedObject();
+ DetailArchiveOperationController result = wrapper.getController();
+ result.rootElement = root;
+
+ return result;
+ }
+
+ public Node getRoot() {
+ return rootElement;
+ }
+
+ private Node rootElement;
+
+ public void set(Vehicle vehicle) {
+ VehiclePaneController controller;
+ try {
+ controller = VehiclePaneController.createVehiclePane();
+ controller.setData(vehicle, false, false);
+ vBoxVehicle.getChildren().add(controller.getRootElement());
+ } catch (IOException e) {
+ LOG.error("IOException in set(Vehicle). (vBoxVehicle) ", e);
+ showServiceExceptionAlertAndWait(
+ "Ein interner Fehler ist aufgetreten. Bitte wenden Sie sich an den/die SystemadministratorIn.");
+ }
+ try {
+ for (int i = 0; i < Objects.requireNonNull(vehicle.registrations()).size(); i++) {
+ Employee employee =
+ Objects.requireNonNull(vehicle.registrations()).get(i).employee();
+
+ EmployeeListItemController employeeListItemController =
+ EmployeeListItemController.createEmployeeListItemController(
+ fxmlLoader, employee);
+ vBoxPeople.getChildren().add(employeeListItemController.getRootElement());
+ }
+ } catch (IOException e) {
+ LOG.error("IOException in set(Vehicle). (vBoxPeople) ", e);
+ showServiceExceptionAlertAndWait(
+ "Ein interner Fehler ist aufgetreten. Bitte wenden Sie sich an den/die SystemadministratorIn.");
+ }
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListController.java
new file mode 100644
index 0000000..12f6bff
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListController.java
@@ -0,0 +1,133 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader.FXMLWrapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.layout.FlowPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Controller;
+
+@Controller
+@Scope("prototype")
+public class EmployeeListController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EmployeeListController.class);
+
+ @FXML private FlowPane flowPaneEmployeeList;
+
+ private Consumer<Employee> onEmployeeClicked;
+
+ private final SpringFXMLLoader fxmlLoader;
+ private Node rootElement;
+ private List<EmployeeListItemController> employeeListItemControllers;
+ private Insets listItemMargins = new Insets(10, 5, 0, 5);
+
+ public EmployeeListController(SpringFXMLLoader fxmlLoader) {
+ this.fxmlLoader = fxmlLoader;
+ this.employeeListItemControllers = new ArrayList<>();
+ }
+
+ public void setListItemMargins(Insets value) {
+ this.listItemMargins = value;
+ }
+
+ public void setData(Set<Employee> employeeList) {
+ setData(employeeList, null, null);
+ }
+
+ public void setData(Set<Employee> employeeList, Consumer<Employee> onEmployeeClicked) {
+ setData(employeeList, onEmployeeClicked, null);
+ }
+
+ public void setData(
+ Set<Employee> employeeList,
+ Consumer<Employee> onEmployeeClicked,
+ Consumer<EmployeeListItemController> onEmployeeListItemClicked) {
+
+ flowPaneEmployeeList.getChildren().clear();
+ employeeListItemControllers.clear();
+ employeeList.forEach(
+ employee ->
+ addEmployeeToFlowPane(
+ employee, onEmployeeClicked, onEmployeeListItemClicked));
+ }
+
+ private void addEmployeeToFlowPane(
+ Employee employee,
+ Consumer<Employee> onEmployeeClicked,
+ Consumer<EmployeeListItemController> onEmployeeListItemClicked) {
+
+ try {
+ EmployeeListItemController controller =
+ EmployeeListItemController.createEmployeeListItemController(
+ fxmlLoader, employee);
+ Node rootElement = controller.getRootElement();
+ flowPaneEmployeeList.getChildren().add(rootElement);
+ employeeListItemControllers.add(controller);
+ FlowPane.setMargin(rootElement, listItemMargins);
+ if (onEmployeeClicked != null) {
+ controller.setConsumerEmployeeClicked(onEmployeeClicked);
+ }
+ if (onEmployeeListItemClicked != null) {
+ controller.setConsumerEmployeeListItemClicked(
+ employeeListItemController -> {
+ onEmployeeListItemClicked.accept(employeeListItemController);
+ if (this.onEmployeeClicked != null) {
+ this.onEmployeeClicked.accept(
+ employeeListItemController.getEmployee());
+ }
+ });
+ }
+ } catch (IOException e) {
+ LOG.error("IOException in addEmployeeToFlowPane. ", e);
+ }
+ }
+
+ private void setEmployeeSelected(Employee employee, boolean selected) {
+ employeeListItemControllers
+ .stream()
+ .filter(controller -> controller.getEmployee().equals(employee))
+ .forEach(controller -> controller.setSelected(selected));
+ }
+
+ public void selectEmployee(Employee employee) {
+ setEmployeeSelected(employee, true);
+ }
+
+ public void deselectEmployee(Employee employee) {
+ setEmployeeSelected(employee, false);
+ }
+
+ public void deselectAllEmployees() {
+ employeeListItemControllers.forEach(
+ employeeListItemController -> employeeListItemController.setSelected(false));
+ }
+
+ public static EmployeeListController createEmployeeListController(SpringFXMLLoader loader)
+ throws IOException {
+ FXMLWrapper<Object, EmployeeListController> wrapper =
+ loader.loadAndWrap("/fxml/employeeList.fxml", EmployeeListController.class);
+ Node root = (Node) wrapper.getLoadedObject();
+ EmployeeListController controller = wrapper.getController();
+ controller.rootElement = root;
+ return controller;
+ }
+
+ public Node getRootElement() {
+ return rootElement;
+ }
+
+ public void setOnEmployeeClicked(Consumer<Employee> onEmployeeClicked) {
+ this.onEmployeeClicked = onEmployeeClicked;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListItemController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListItemController.java
new file mode 100644
index 0000000..543fe0d
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/EmployeeListItemController.java
@@ -0,0 +1,87 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader.FXMLWrapper;
+import java.io.IOException;
+import java.util.function.Consumer;
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Controller;
+
+@Controller
+@Scope("prototype")
+public class EmployeeListItemController extends CustomListItemController {
+
+ @FXML private Label lblName;
+ @FXML private Label lblQualification;
+ @FXML private Label lblPilot;
+ @FXML private Label lblDriver;
+ @FXML private ImageView imgPilot;
+ @FXML private ImageView imgDriver;
+ @FXML private ImageView imgQualification;
+
+ private Employee employee;
+
+ private Consumer<Employee> consumerEmployeeClicked;
+ private Consumer<EmployeeListItemController> consumerEmployeeListItemClicked;
+
+ @FXML
+ private void onEmployeeClicked() {
+ if (consumerEmployeeClicked != null) {
+ consumerEmployeeClicked.accept(employee);
+ }
+ if (consumerEmployeeListItemClicked != null) {
+ consumerEmployeeListItemClicked.accept(this);
+ }
+ }
+
+ private void setData(Employee employee) {
+ this.employee = employee;
+ lblName.setText(employee.name());
+ lblQualification.setText(employee.educationLevel().name());
+ lblPilot.setText(String.format("%s Pilot", employee.isPilot() ? "ist" : "nicht"));
+ lblDriver.setText(String.format("%s Fahrer", employee.isDriver() ? "ist" : "nicht"));
+ imgQualification.setImage(new Image("/images/Qualification.png"));
+ String imgSrcPilot =
+ String.format("/images/%s", employee.isPilot() ? "Pilot.png" : "NotPilot.png");
+ imgPilot.setImage(new Image(imgSrcPilot));
+ String imgSrcDriver =
+ String.format("/images/%s", employee.isDriver() ? "Driver.png" : "NotDriver.png");
+ imgDriver.setImage(new Image(imgSrcDriver));
+ }
+
+ public static EmployeeListItemController createEmployeeListItemController(
+ SpringFXMLLoader fxmlLoader, Employee employee) throws IOException {
+ EmployeeListItemController controller = createEmployeeListItemController(fxmlLoader);
+ controller.setData(employee);
+ return controller;
+ }
+
+ public static EmployeeListItemController createEmployeeListItemController(
+ SpringFXMLLoader loader) throws IOException {
+ FXMLWrapper<Object, EmployeeListItemController> wrapper =
+ loader.loadAndWrap("/fxml/employeeListItem.fxml", EmployeeListItemController.class);
+ Node root = (Node) wrapper.getLoadedObject();
+ EmployeeListItemController controller = wrapper.getController();
+ controller.rootElement = root;
+ return controller;
+ }
+
+ public Employee getEmployee() {
+ return employee;
+ }
+
+ public void setConsumerEmployeeClicked(Consumer<Employee> consumerEmployeeClicked) {
+ this.consumerEmployeeClicked = consumerEmployeeClicked;
+ }
+
+ public void setConsumerEmployeeListItemClicked(
+ Consumer<EmployeeListItemController> consumerEmployeeListItemClicked) {
+ this.consumerEmployeeListItemClicked = consumerEmployeeListItemClicked;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/FilterEmployeesController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/FilterEmployeesController.java
new file mode 100644
index 0000000..a31c3e3
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/FilterEmployeesController.java
@@ -0,0 +1,65 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader.FXMLWrapper;
+import java.io.IOException;
+import java.util.function.Consumer;
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.TextField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Controller;
+
+@Controller
+@Scope("prototype")
+public class FilterEmployeesController {
+ private static final Logger LOG = LoggerFactory.getLogger(FilterEmployeesController.class);
+
+ @FXML private TextField inputFilterString;
+
+ private Consumer<String> consumerFilterTextChanged;
+ private Runnable consumerAddEmployeeClicked;
+
+ private Node rootElement;
+
+ @FXML
+ private void onAddEmployeeClicked() {
+ LOG.debug("Button \"Person hinzufügen\" clicked.");
+ if (consumerAddEmployeeClicked != null) {
+ consumerAddEmployeeClicked.run();
+ }
+ }
+
+ @FXML
+ private void onFilterTextChanged() {
+ LOG.debug("Filter text changed.");
+ if (consumerFilterTextChanged != null) {
+ consumerFilterTextChanged.accept(inputFilterString.getText());
+ }
+ }
+
+ public void setOnFilterTextChangedListener(Consumer<String> callback) {
+ this.consumerFilterTextChanged = callback;
+ }
+
+ public void setOnAddEmployeeClickedListener(Runnable callback) {
+ this.consumerAddEmployeeClicked = callback;
+ }
+
+ public static FilterEmployeesController createFilterEmployeesController(
+ SpringFXMLLoader fxmlLoader) throws IOException {
+ FXMLWrapper<Object, FilterEmployeesController> wrapper =
+ fxmlLoader.loadAndWrap(
+ "/fxml/filterEmployeesControl.fxml", FilterEmployeesController.class);
+ Node root = (Node) wrapper.getLoadedObject();
+ FilterEmployeesController controller = wrapper.getController();
+ controller.rootElement = root;
+ return controller;
+ }
+
+ public Node getRootElement() {
+ return rootElement;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/Helper.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/Helper.java
new file mode 100644
index 0000000..f120eb6
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/Helper.java
@@ -0,0 +1,34 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.ButtonType;
+
+public class Helper {
+
+ static final String ALERT_TITLE_VALIDATION_ERROR = "Validierungsfehler";
+ static final String ALERT_TITLE_SERVICE_EXCEPTION = "Fehler";
+ static final String ALERT_TITLE_SUCCESS = "Erfolg";
+
+ private Helper() {} // SonarLint insisted to create a private constructor to hide the public one
+
+ static void showValidationErrorAlertAndWait(String message) {
+ showAlertWithOkButtonAndWait(AlertType.ERROR, ALERT_TITLE_VALIDATION_ERROR, message);
+ }
+
+ static void showServiceExceptionAlertAndWait(String message) {
+ showAlertWithOkButtonAndWait(AlertType.ERROR, ALERT_TITLE_SERVICE_EXCEPTION, message);
+ }
+
+ static void showSuccessAlertAndWait(String message) {
+ showAlertWithOkButtonAndWait(AlertType.INFORMATION, ALERT_TITLE_SUCCESS, message);
+ }
+
+ static void showAlertWithOkButtonAndWait(
+ AlertType alertType, String headerText, String contentText) {
+ Alert alert = new Alert(alertType, contentText, ButtonType.OK);
+ alert.setTitle(headerText);
+ alert.setHeaderText(headerText);
+ alert.showAndWait();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ManageEmployeesController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ManageEmployeesController.java
new file mode 100644
index 0000000..6138094
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/ManageEmployeesController.java
@@ -0,0 +1,120 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.EmployeeService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.stream.Collectors;
+import javafx.fxml.FXML;
+import javafx.scene.layout.AnchorPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class ManageEmployeesController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ManageEmployeesController.class);
+ @FXML private AnchorPane listEmployeesAP;
+ @FXML private AnchorPane containerHeader;
+ @FXML private EmployeeListController employeeListController;
+
+ private final EmployeeService employeeService;
+ private final SpringFXMLLoader fxmlLoader;
+
+ private final CreateOperationController createOperationController;
+
+ public ManageEmployeesController(
+ EmployeeService employeeService,
+ SpringFXMLLoader fxmlLoader,
+ CreateOperationController createOperationController) {
+ this.employeeService = employeeService;
+ this.fxmlLoader = fxmlLoader;
+ this.createOperationController = createOperationController;
+ }
+
+ @FXML
+ private void initialize() {
+ openFilter();
+ }
+
+ private void openFilter() {
+ try {
+ FilterEmployeesController filterEmployeesController =
+ FilterEmployeesController.createFilterEmployeesController(fxmlLoader);
+ containerHeader.getChildren().clear();
+ containerHeader.getChildren().add(filterEmployeesController.getRootElement());
+ filterEmployeesController.setOnFilterTextChangedListener(this::updateEmployeeList);
+ filterEmployeesController.setOnAddEmployeeClickedListener(this::openAddEmployee);
+ updateEmployeeList();
+
+ } catch (IOException e) {
+ LOG.error("IOException in openFilter().", e);
+ }
+ }
+
+ private void openAddEmployee() {
+ employeeListController.deselectAllEmployees();
+ openEmployee(null);
+ }
+
+ private void openEditEmployee(Employee employee) {
+ employeeListController.deselectAllEmployees();
+ employeeListController.selectEmployee(employee);
+ openEmployee(employee);
+ }
+
+ private void openEmployee(Employee employee) {
+ try {
+ CreateNewEmployeeController createNewEmployeeController =
+ employee == null
+ ? CreateNewEmployeeController.createCreateNewEmployeeController(
+ fxmlLoader)
+ : CreateNewEmployeeController.createCreateNewEmployeeController(
+ fxmlLoader, employee);
+ containerHeader.getChildren().clear();
+ containerHeader.getChildren().add(createNewEmployeeController.getRootElement());
+ createNewEmployeeController.setConsumerCancelClicked(this::openFilter);
+ createNewEmployeeController.setConsumerCreateClicked(this::openFilter);
+ } catch (IOException e) {
+ LOG.error("IOException in openEmployee(). ", e);
+ }
+ }
+
+ private void updateEmployeeList() {
+ updateEmployeeList("");
+ }
+
+ private void updateEmployeeList(String searchString) {
+
+ try {
+ employeeListController.setData(
+ employeeService
+ .list()
+ .stream()
+ .filter(
+ employee ->
+ searchString.trim().isEmpty()
+ || employee.name()
+ .toLowerCase()
+ .contains(searchString.toLowerCase()))
+ .collect(Collectors.toCollection(HashSet::new)),
+ this::openEditEmployee);
+
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in updateEmployeeList(). ", e);
+ }
+ }
+
+ public void setVisible(boolean b) {
+ listEmployeesAP.setVisible(b);
+ }
+
+ public void backToMain() {
+ LOG.debug("Hyperlink \"Zurück\" clicked.");
+ this.setVisible(false);
+ createOperationController.setVisible(true);
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationDetailsController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationDetailsController.java
new file mode 100644
index 0000000..0476fc6
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationDetailsController.java
@@ -0,0 +1,169 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showAlertWithOkButtonAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showSuccessAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showValidationErrorAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.OperationService;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.VehicleService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.FlowPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class OperationDetailsController {
+ private static final Logger LOG = LoggerFactory.getLogger(OperationDetailsController.class);
+
+ public Operation operation;
+ private final OperationService operationService;
+ private final VehicleService vehicleService;
+ private final CreateOperationController createOperationController;
+ @FXML private FlowPane fpVehicles;
+ @FXML private ListView<Operation> lvActiveOperations;
+ @FXML private Label lblChosenVehicles;
+ @FXML private Button btnCloseOperation;
+ @FXML private Button btnCancelOperation;
+ @FXML private Label lblCode, lblAdditionalInfo, lblAddress;
+ @FXML private AnchorPane operationDetailsAP;
+
+ public OperationDetailsController(
+ OperationService operationService,
+ VehicleService vehicleService,
+ CreateOperationController createOperationController) {
+ this.operationService = operationService;
+ this.vehicleService = vehicleService;
+ this.createOperationController = createOperationController;
+ }
+
+ @FXML
+ private void initialize() {
+ lvActiveOperations.setCellFactory(
+ param -> CreateOperationController.generateOpCodeListItem());
+ lvActiveOperations.setOnMouseClicked(
+ event -> {
+ if (event.getClickCount() == 2) {
+ if (lvActiveOperations.getSelectionModel().getSelectedItem() == null) {
+ return;
+ }
+ initOperation(lvActiveOperations.getSelectionModel().getSelectedItem());
+ }
+ });
+ }
+
+ private void updateFlowPane() {
+ try {
+ fpVehicles.getChildren().clear();
+ for (Vehicle vehicle : operation.vehicles()) {
+ VehiclePaneController controller = VehiclePaneController.createVehiclePane();
+ controller.setData(vehicle, true, true);
+ controller.getBtnRequest().setOnAction(e -> requestVehicleClicked(controller));
+ fpVehicles.getChildren().add(controller.getRootElement());
+ }
+ } catch (IOException e) {
+ LOG.error("Error while updating list.", e);
+ showServiceExceptionAlertAndWait("Error while updating list.");
+ }
+ }
+
+ void initOperation(Operation operation) {
+ fillActiveList();
+ this.operation = operation;
+ lblCode.setText(operation.opCode());
+ Collection<String> vehicleNames =
+ operation.vehicles().stream().map(Vehicle::name).collect(Collectors.toList());
+ String result = String.join(", ", vehicleNames);
+ lblChosenVehicles.setText(result.toString());
+ lblAdditionalInfo.setText(operation.additionalInfo());
+ lblAddress.setText(operation.destination());
+ updateFlowPane();
+ operationDetailsAP.setVisible(true);
+ }
+
+ private void fillActiveList() {
+ try {
+ lvActiveOperations.setItems(
+ FXCollections.observableArrayList(
+ operationService.list(EnumSet.of(Status.ACTIVE))));
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in fillActiveList(). ", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ }
+ }
+
+ @FXML
+ public void closeOperationClicked() {
+ LOG.debug("Button \"Abschließen\" clicked.");
+ try {
+ operationService.complete(operation.id(), Status.COMPLETED);
+ } catch (InvalidOperationException e) {
+ LOG.debug("Validation error in closeOperationClicked(). ", e);
+ showAlertWithOkButtonAndWait(AlertType.ERROR, "Validierungsfehler", e.getMessage());
+ return;
+ } catch (ServiceException e) {
+ LOG.error("Exception in closeOperationClicked(). ", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ return;
+ }
+ showSuccessAlertAndWait("Der Einsatz wurde erfolgreich aktualisiert");
+ createOperationController.updateList();
+ closeWindow();
+ }
+
+ public void cancelOperationClicked() {
+ LOG.debug("Button \"Stornieren\" clicked.");
+ try {
+ operationService.complete(operation.id(), Status.CANCELLED);
+ } catch (InvalidOperationException e) {
+ LOG.debug("Validation error in cancelOperationClicked(). ", e);
+ showValidationErrorAlertAndWait(e.getMessage());
+ return;
+ } catch (ServiceException e) {
+ LOG.error("Exception in cancelOperationClicked(). ", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ return;
+ }
+ showSuccessAlertAndWait("Der Einsatz wurde erfolgreich aktualisiert");
+ createOperationController.updateList();
+ closeWindow();
+ }
+
+ private void requestVehicleClicked(VehiclePaneController v) {
+ LOG.debug("Button \"Nachfordern\" clicked.");
+
+ try {
+ operationService.requestVehicles(operation.id(), Set.of(v.getData().id()));
+ } catch (ServiceException | InvalidOperationException | InvalidVehicleException e) {
+ LOG.error("Exception in requestVehicleClicked()", e);
+ showServiceExceptionAlertAndWait(e.getMessage());
+ return;
+ }
+ showSuccessAlertAndWait("Das Fahrzeug wurde erfolgreich angefordert");
+ createOperationController.updateList();
+ }
+
+ public void closeWindow() {
+ LOG.debug("Hyperlink \"Zurück\" clicked.");
+ operationDetailsAP.setVisible(false);
+ this.createOperationController.setVisible(true);
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationInArchiveController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationInArchiveController.java
new file mode 100644
index 0000000..17f0f55
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/OperationInArchiveController.java
@@ -0,0 +1,65 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.text.Text;
+
+public class OperationInArchiveController {
+
+ @FXML private Text txtAddress;
+ @FXML private Text txtVehicles;
+ @FXML private Text txtDate;
+ @FXML private Text txtOpCode;
+
+ static OperationInArchiveController create() throws IOException {
+ FXMLLoader fxmlLoader =
+ new FXMLLoader(
+ OperationInArchiveController.class.getResource(
+ "/fxml/OperationInArchive.fxml"));
+ Node root = fxmlLoader.load();
+ OperationInArchiveController result = fxmlLoader.getController();
+ result.rootElement = root;
+
+ return result;
+ }
+
+ public Node getRoot() {
+ return rootElement;
+ }
+
+ private Node rootElement;
+
+ public void set(Operation operation) {
+ txtAddress.setText(operation.destination());
+ String date = "am ";
+ if (operation.created() != null) {
+ LocalDateTime myDateTime =
+ LocalDateTime.ofInstant(
+ Objects.requireNonNull(operation.created()), ZoneOffset.UTC);
+ date +=
+ myDateTime.getDayOfMonth()
+ + "."
+ + myDateTime.getMonth().getValue()
+ + "."
+ + myDateTime.getYear();
+ txtDate.setText(date);
+ } else {
+ txtDate.setText("---");
+ }
+ txtOpCode.setText(operation.opCode());
+ Collection<String> elements =
+ operation.vehicles().stream().map(Vehicle::name).collect(Collectors.toList());
+ String result = String.join(", ", elements);
+
+ txtVehicles.setText(result);
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/RegistrationWindowController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/RegistrationWindowController.java
new file mode 100644
index 0000000..9c47fe4
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/RegistrationWindowController.java
@@ -0,0 +1,279 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showServiceExceptionAlertAndWait;
+import static at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller.Helper.showValidationErrorAlertAndWait;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.EmployeeService;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.RegistrationService;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service.VehicleService;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.VBox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class RegistrationWindowController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RegistrationWindowController.class);
+
+ private final EmployeeService employeeService;
+ private final VehicleService vehicleService;
+ private final RegistrationService registrationService;
+ private final CreateOperationController createOperationController;
+ private final SpringFXMLLoader fxmlLoader;
+
+ @FXML private GridPane root;
+ @FXML private VBox vbVehicles;
+ @FXML private ScrollPane listEmployee;
+ @FXML private ChoiceBox<Integer> cbStart;
+ @FXML private ChoiceBox<Integer> cbEnd;
+ @FXML private Label lVehicles;
+ @FXML private Label lEmployees;
+ @FXML private TextField tfVehicleSearch;
+ @FXML private TextField tfEmployeeSearch;
+ private EmployeeListController employeeListController;
+
+ private Vehicle chosenVehicle;
+ private List<Employee> chosenEmployees = new LinkedList<>();
+
+ public RegistrationWindowController(
+ EmployeeService employeeService,
+ VehicleService vehicleService,
+ CreateOperationController createOperationController,
+ RegistrationService registrationService,
+ SpringFXMLLoader fxmlLoader) {
+ this.employeeService = employeeService;
+ this.vehicleService = vehicleService;
+ this.createOperationController = createOperationController;
+ this.registrationService = registrationService;
+ this.fxmlLoader = fxmlLoader;
+ }
+
+ @FXML
+ private void initialize() throws IOException {
+ employeeListController = EmployeeListController.createEmployeeListController(fxmlLoader);
+ employeeListController.setListItemMargins(new Insets(10, 6, 0, 6));
+ // listEmployee. .getChildren().add(employeeListController.getRootElement());
+ Node emplList = employeeListController.getRootElement();
+ // emplList.(360);
+ listEmployee.setContent(emplList);
+
+ ObservableList<Integer> hours =
+ FXCollections.observableArrayList(
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23);
+ cbStart.setItems(hours);
+ cbStart.setValue(0);
+ cbEnd.setItems(hours);
+ cbEnd.setValue(12);
+
+ reset();
+ }
+
+ private void updateEmplList() {
+ employeeListController.deselectAllEmployees();
+
+ try {
+ Set<Employee> employees =
+ employeeService
+ .list()
+ .stream()
+ .filter(
+ e ->
+ e.name()
+ .toLowerCase()
+ .contains(
+ tfEmployeeSearch
+ .getText()
+ .toLowerCase()))
+ .collect(Collectors.toCollection(HashSet::new));
+ employeeListController.setData(
+ employees,
+ selection -> {
+ if (selection == null) {
+ return;
+ } else if (chosenEmployees.contains(selection)) {
+ chosenEmployees.remove(selection);
+ } else {
+ chosenEmployees.add(selection);
+ }
+
+ StringBuilder text = new StringBuilder();
+ boolean first = true;
+ for (Employee employee : chosenEmployees) {
+ if (!first) {
+ text.append(", ");
+ }
+ text.append(employee.name());
+ first = false;
+ }
+ lEmployees.setText(text.toString());
+ },
+ contr -> contr.setSelected(chosenEmployees.contains(contr.getEmployee())));
+
+ employees.forEach(
+ e -> {
+ if (chosenEmployees.contains(e)) employeeListController.selectEmployee(e);
+ });
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in updateEmplList(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Auflisten des Personals ist ein Fehler aufgetreten.");
+ }
+ }
+
+ private void updateVehList() {
+ vbVehicles.getChildren().clear();
+
+ try {
+ Set<Vehicle> vehicles = vehicleService.list(EnumSet.of(Status.ABGEMELDET));
+
+ boolean anyMatch = false;
+
+ for (Vehicle vehicle : vehicles) {
+ if (!vehicle.name().toLowerCase().contains(tfVehicleSearch.getText().toLowerCase()))
+ continue;
+
+ anyMatch = true;
+
+ VehiclePaneController vp = VehiclePaneController.createVehiclePane();
+ vp.setData(vehicle, false, false);
+ vbVehicles.getChildren().add(vp.getRootElement());
+
+ vp.getRootElement()
+ .setOnMouseClicked(
+ event -> {
+ if (event.getButton() == MouseButton.PRIMARY) {
+ chosenVehicle = vehicle;
+ lVehicles.setText(chosenVehicle.name());
+ updateVehList();
+ }
+ });
+ if (chosenVehicle != null && chosenVehicle.id() == vehicle.id())
+ vp.setSelected(true);
+ }
+
+ if (!anyMatch) {
+ // Kind of ugly, but best way to get the size of a VehiclePane
+ VehiclePaneController vp = VehiclePaneController.createVehiclePane();
+ vp.getRootElement().setVisible(false);
+ vbVehicles.getChildren().add(vp.getRootElement());
+ }
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in updateVehList(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Auflisten der Fahrzeuge ist ein Fehler aufgetreten");
+ } catch (IOException e) {
+ LOG.error("IOException in updateVehList(). ", e);
+ showServiceExceptionAlertAndWait("Beim Laden der Fahrzeuge ist ein Fehler aufgetreten");
+ }
+ }
+
+ public void cancel() {
+ LOG.debug("Hyperlink \"schließen\" clicked");
+ reset();
+ this.setVisible(false);
+ createOperationController.setVisible(true);
+ }
+
+ private void reset() {
+ chosenEmployees.clear();
+ chosenVehicle = null;
+ tfEmployeeSearch.setText("");
+ tfVehicleSearch.setText("");
+ lEmployees.setText("-");
+ lVehicles.setText("-");
+ updateVehList();
+ updateEmplList();
+ }
+
+ public void create() {
+ LOG.debug("Button \"ERSTELLEN\" clicked");
+
+ Set<Registration> registrations = new HashSet<>();
+ try {
+ if (chosenVehicle == null) {
+ throw new InvalidVehicleException("no Vehicle");
+ }
+ for (Employee employee : chosenEmployees) {
+ registrations.add(
+ Registration.builder()
+ .id(chosenVehicle.id())
+ .employee(employee)
+ .start(
+ LocalDateTime.of(
+ LocalDate.now(),
+ LocalTime.of(cbStart.getValue(), 0))
+ .toInstant(OffsetDateTime.now().getOffset()))
+ .end(
+ LocalDateTime.of(
+ LocalDate.now(),
+ LocalTime.of(cbEnd.getValue(), 0))
+ .toInstant(OffsetDateTime.now().getOffset()))
+ .build());
+ }
+
+ registrationService.add(chosenVehicle.id(), registrations);
+ chosenEmployees.clear();
+ // ((Stage) lVehicles.getScene().getWindow()).close();
+ this.setVisible(false);
+ createOperationController.setVisible(true);
+ reset();
+ } catch (InvalidVehicleException e) {
+ LOG.debug("Validation of Vehicle in Registration failed.");
+ showValidationErrorAlertAndWait("Das spezifizierte Fahrzeug ist nicht gültig.");
+ } catch (ServiceException e) {
+ LOG.error("ServiceException in create(). ", e);
+ showServiceExceptionAlertAndWait(
+ "Beim Erstellen der Anmeldung ist ein Fehler aufgetreten.");
+ } catch (InvalidRegistrationException e) {
+ LOG.debug("Validation of Registration failed.");
+ showValidationErrorAlertAndWait(
+ "Die gewählte Kombination von Fahrzeug und Personal ist nicht gültig!");
+ }
+ }
+
+ public void setVisible(boolean b) {
+ root.setVisible(b);
+ reset();
+ }
+
+ public void tfVehicleSearch_TextChanged(KeyEvent keyEvent) {
+ updateVehList();
+ }
+
+ public void tfEmployeeSearch_TextChanged(KeyEvent keyEvent) {
+ updateEmplList();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/VehiclePaneController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/VehiclePaneController.java
new file mode 100644
index 0000000..66b45d2
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/controller/VehiclePaneController.java
@@ -0,0 +1,118 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.controller;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee.EducationLevel;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.text.Text;
+
+public class VehiclePaneController extends CustomListItemController {
+
+ public static VehiclePaneController createVehiclePane() throws IOException {
+ FXMLLoader fxmlLoader =
+ new FXMLLoader(VehiclePaneController.class.getResource("/fxml/vehiclePane.fxml"));
+ Node root = fxmlLoader.load();
+ VehiclePaneController result = fxmlLoader.getController();
+ result.rootElement = root;
+
+ return result;
+ }
+
+ @FXML private Label txtStatus;
+ @FXML private Text txtType;
+ @FXML private Text txtNumber;
+ @FXML private ImageView ivNEF;
+ @FXML private Text txtNEF;
+ @FXML private ImageView ivQualification;
+ @FXML private Text txtQualification;
+ @FXML private Text txtRooftype;
+ @FXML private Button btnRequest;
+
+ private Vehicle data;
+
+ public Vehicle getData() {
+ return data;
+ }
+
+ /**
+ * * Set the displayed data of this VehiclePane.
+ *
+ * @param vehicle The data to display.
+ * @param showStatusInfo If true, the highest qualification of the vehicle's active registration
+ * and the vehicle's status will be shown.
+ */
+ public void setData(Vehicle vehicle, boolean showStatusInfo, boolean showRequestVehicle) {
+ txtType.setText(vehicle.type().name());
+ String constrType = vehicle.constructionType().name();
+ txtRooftype.setText(
+ constrType.substring(0, 1).toUpperCase() + constrType.substring(1).toLowerCase());
+ txtNumber.setText("-" + vehicle.id());
+ if (vehicle.hasNef()) {
+ ivNEF.setImage(new Image("images/NEF.png"));
+ txtNEF.setText("hat NEF-Halterung");
+ } else {
+ ivNEF.setImage(new Image("images/NotNEF.png"));
+ txtNEF.setText("keine NEF-Halterung");
+ }
+
+ if (showRequestVehicle) {
+ btnRequest.setVisible(true);
+ btnRequest.setManaged(true);
+ } else {
+ btnRequest.setVisible(false);
+ btnRequest.setManaged(false);
+ }
+
+ if (showStatusInfo) {
+ txtStatus.setText(vehicle.status().name());
+ if (vehicle.status() == Status.FREI_FUNK || vehicle.status() == Status.FREI_WACHE) {
+ txtStatus.getStyleClass().add("bg-status-green");
+ } else {
+ txtStatus.getStyleClass().add("bg-status-orange");
+ }
+
+ Instant now = Instant.now();
+ List<Registration> regs = vehicle.registrations();
+
+ if (regs == null) {
+ return;
+ }
+
+ Optional<EducationLevel> edu =
+ regs.stream()
+ .filter(reg -> reg.start().isBefore(now) && reg.end().isAfter(now))
+ .map(reg -> reg.employee().educationLevel())
+ .max(EducationLevel::compareTo);
+
+ if (!edu.isPresent()) {
+ return;
+ }
+
+ txtQualification.setText(edu.get().name());
+ } else {
+ txtQualification.setVisible(false);
+ txtQualification.setManaged(false);
+ ivQualification.setVisible(false);
+ ivQualification.setManaged(false);
+
+ txtStatus.setVisible(false);
+ }
+
+ this.data = vehicle;
+ }
+
+ public Button getBtnRequest() {
+ return btnRequest;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDAO.java
new file mode 100644
index 0000000..d8ac513
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDAO.java
@@ -0,0 +1,44 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import java.util.Set;
+
+public interface EmployeeDAO {
+
+ /**
+ * Persist the given employee.
+ *
+ * @param employee that should be stored
+ * @return the id that was assigned
+ * @throws PersistenceException if the employee could not be persisted
+ */
+ long add(Employee employee) throws PersistenceException;
+
+ /**
+ * Update the given employee.
+ *
+ * @param employee that should be updated
+ * @throws ElementNotFoundException if no employee with the given id exists
+ * @throws PersistenceException if the employee could not be updated
+ */
+ void update(Employee employee) throws ElementNotFoundException, PersistenceException;
+
+ /**
+ * Get all stored employees.
+ *
+ * @return list containing all stored employees
+ * @throws PersistenceException if loading the stored employees failed
+ */
+ Set<Employee> list() throws PersistenceException;
+
+ /**
+ * Remove employee with the given id from the store.
+ *
+ * @param id of the employee that should be removed
+ * @throws ElementNotFoundException if no employee with the given id exists
+ * @throws PersistenceException if the employee could not be removed
+ */
+ void remove(long id) throws ElementNotFoundException, PersistenceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDatabaseDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDatabaseDAO.java
new file mode 100644
index 0000000..889b0fc
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/EmployeeDatabaseDAO.java
@@ -0,0 +1,144 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee.EducationLevel;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class EmployeeDatabaseDAO implements EmployeeDAO {
+
+ private JDBCConnectionManager jdbcConnectionManager;
+
+ public EmployeeDatabaseDAO(JDBCConnectionManager jdbcConnectionManager) {
+ this.jdbcConnectionManager = jdbcConnectionManager;
+ }
+
+ private long createEmployeeVersion(Connection con, Employee e)
+ throws PersistenceException, SQLException {
+ String sql =
+ "INSERT INTO EmployeeVersion(name, birthday, educationLevel, isDriver, isPilot) "
+ + "VALUES(?, ?, ?, ?, ?)";
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setString(1, e.name());
+ pstmt.setObject(2, e.birthday());
+ pstmt.setInt(3, e.educationLevel().ordinal());
+ pstmt.setBoolean(4, e.isDriver());
+ pstmt.setBoolean(5, e.isPilot());
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (!rs.next()) throw new PersistenceException("Failed to insert EmployeeVersion");
+
+ return rs.getLong(1);
+ }
+ }
+ }
+
+ @Override
+ public long add(Employee employee) throws PersistenceException {
+ String sql = "INSERT INTO Employee(version) VALUES(?)";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+
+ long versionId = createEmployeeVersion(con, employee);
+
+ try (PreparedStatement pstmt =
+ con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setLong(1, versionId);
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (!rs.next()) {
+ con.rollback();
+ throw new PersistenceException("Failed to insert Employee");
+ }
+
+ con.commit();
+ return rs.getLong(1);
+ }
+ }
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void update(Employee employee) throws ElementNotFoundException, PersistenceException {
+ String sql = "UPDATE Employee SET version = ? WHERE id = ?";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+
+ long versionId = createEmployeeVersion(con, employee);
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, versionId);
+ pstmt.setLong(2, employee.id());
+
+ if (pstmt.executeUpdate() != 1) {
+ con.rollback();
+ throw new ElementNotFoundException("No such employeeId exists");
+ }
+ }
+
+ con.commit();
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public Set<Employee> list() throws PersistenceException {
+ String sql =
+ "SELECT emp.id, v.name, v.birthday, v.educationLevel, v.isDriver, v.isPilot "
+ + "FROM employee emp "
+ + "JOIN EmployeeVersion v ON v.id = emp.version";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ Set<Employee> employees = new HashSet<>();
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ try (ResultSet rs = pstmt.executeQuery()) {
+ while (rs.next()) {
+ employees.add(
+ Employee.builder()
+ .id(rs.getLong(1))
+ .name(rs.getString(2))
+ .birthday(rs.getObject(3, LocalDate.class))
+ .educationLevel(EducationLevel.valueOf(rs.getString(4)))
+ .isDriver(rs.getBoolean(5))
+ .isPilot(rs.getBoolean(6))
+ .build());
+ }
+ }
+ }
+
+ return employees;
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void remove(long id) throws ElementNotFoundException, PersistenceException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDAO.java
new file mode 100644
index 0000000..c0ef5d4
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDAO.java
@@ -0,0 +1,48 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import java.util.EnumSet;
+import java.util.Set;
+
+public interface OperationDAO {
+
+ /**
+ * Persist the given operation.
+ *
+ * @param operation that should be stored
+ * @return the id that was assigned
+ * @throws PersistenceException if the operation could not be persisted
+ */
+ long add(Operation operation) throws PersistenceException;
+
+ /**
+ * Update the given operation.
+ *
+ * @param operation that should be updated
+ * @throws ElementNotFoundException if no operation with the given id exists
+ * @throws PersistenceException if the operation could not be updated
+ */
+ void update(Operation operation) throws ElementNotFoundException, PersistenceException;
+
+ /**
+ * Returns the operation with the given id.
+ *
+ * @param operationId id of the operation that should be returned
+ * @return operation with the given id
+ * @throws ElementNotFoundException if no operation with the given id exists
+ * @throws PersistenceException if the operation could not be loaded
+ */
+ Operation get(long operationId) throws ElementNotFoundException, PersistenceException;
+
+ /**
+ * Get all stored operations with matching status.
+ *
+ * @param statuses set containing all statuses that should be matched
+ * @return list containing all matched operations
+ * @throws PersistenceException if loading the stored operations failed
+ */
+ Set<Operation> list(EnumSet<Status> statuses) throws PersistenceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDatabaseDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDatabaseDAO.java
new file mode 100644
index 0000000..1641720
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/OperationDatabaseDAO.java
@@ -0,0 +1,221 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Severity;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class OperationDatabaseDAO implements OperationDAO {
+
+ private JDBCConnectionManager jdbcConnectionManager;
+ private VehicleDAO vehicleDAO;
+
+ public OperationDatabaseDAO(
+ JDBCConnectionManager jdbcConnectionManager, VehicleDAO vehicleDAO) {
+ this.jdbcConnectionManager = jdbcConnectionManager;
+ this.vehicleDAO = vehicleDAO;
+ }
+
+ @Override
+ public long add(@NonNull Operation o) throws PersistenceException {
+ String sql =
+ "INSERT INTO Operation(opCode, severity, created, destination, additionalInfo,"
+ + " status) VALUES (?, ?, ?, ?, ?, ?)";
+ String sql2 = "INSERT INTO VehicleOperation(vehicleId, operationId) VALUES (?, ?)";
+ long operationId;
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setString(1, o.opCode());
+ pstmt.setInt(2, o.severity().ordinal());
+ pstmt.setObject(3, OffsetDateTime.ofInstant(o.created(), ZoneId.systemDefault()));
+ pstmt.setString(4, o.destination());
+ pstmt.setString(5, o.additionalInfo());
+ pstmt.setInt(6, o.status().ordinal());
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (!rs.next()) throw new PersistenceException("Failed to persist operation");
+
+ operationId = rs.getLong(1);
+ }
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql2)) {
+ pstmt.setLong(2, operationId);
+
+ for (long id : (Iterable<Long>) o.vehicles().stream().map(Vehicle::id)::iterator) {
+ pstmt.setLong(1, id);
+ pstmt.addBatch();
+ }
+
+ pstmt.executeBatch();
+ }
+ con.commit();
+ return operationId;
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void update(@NonNull Operation o) throws ElementNotFoundException, PersistenceException {
+ // Note this will, by design, not update created
+ String sql =
+ "UPDATE Operation SET opCode = ?, severity = ?, destination = ?,"
+ + " additionalInfo = ?, status = ? WHERE id = ?";
+ String sql2 = "DELETE FROM VehicleOperation WHERE operationId = ?";
+ String sql3 = "INSERT INTO VehicleOperation(vehicleId, operationId) VALUES (?, ?)";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setString(1, o.opCode());
+ pstmt.setInt(2, o.severity().ordinal());
+ pstmt.setString(3, o.destination());
+ pstmt.setString(4, o.additionalInfo());
+ pstmt.setInt(5, o.status().ordinal());
+ pstmt.setLong(6, o.id());
+
+ if (pstmt.executeUpdate() != 1)
+ throw new ElementNotFoundException("No such operationId exists");
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql2)) {
+ pstmt.setLong(1, o.id());
+ pstmt.executeUpdate();
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql3)) {
+ pstmt.setLong(2, o.id());
+
+ for (long id : (Iterable<Long>) o.vehicles().stream().map(Vehicle::id)::iterator) {
+ pstmt.setLong(1, id);
+ pstmt.addBatch();
+ }
+
+ pstmt.executeBatch();
+ }
+ con.commit();
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public Operation get(long operationId) throws ElementNotFoundException, PersistenceException {
+ String sql = "Select * from operation where id = ?";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, operationId);
+ pstmt.execute();
+
+ try (ResultSet rs = pstmt.getResultSet()) {
+ if (!rs.next())
+ throw new ElementNotFoundException("No such element could be found");
+
+ return operationFromRS(rs);
+ }
+ }
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public Set<Operation> list(EnumSet<Status> statuses) throws PersistenceException {
+ // This hack exists because H2 currently has a bug that prevents IN (?) with an array of
+ // ids, i.e. pstmt.setArray(1, con.createArrayOf("INT", intarray) from working. See
+ // commented code below.
+
+ // SELECT * FROM Operation WHERE status IN ('COMPLETED', 'CANCELLED') is BUGGED on H2!
+ // for this reason we use the ordinal values instead
+ String str =
+ statuses.stream()
+ .map(e -> Integer.toString(e.ordinal()))
+ .collect(Collectors.joining(","));
+
+ String sql = "SELECT * FROM Operation WHERE status IN (" + str + ")";
+ Set<Operation> operations = new HashSet<>();
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ // Object[] arr = statuses.stream().map(Enum::ordinal).toArray();
+ // pstmt.setArray(1, con.createArrayOf("INT", arr));
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ while (rs.next()) operations.add(operationFromRS(rs));
+ }
+ }
+
+ return operations;
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ private Operation operationFromRS(ResultSet rs) throws PersistenceException, SQLException {
+ Long operationId = rs.getLong("id");
+
+ return Operation.builder()
+ .id(operationId)
+ .opCode(rs.getString("opCode"))
+ .severity(Severity.valueOf(rs.getString("severity")))
+ .status(Status.valueOf(rs.getString("status")))
+ .vehicles(getVehiclesFromOperationId(operationId))
+ .created((rs.getObject("created", OffsetDateTime.class)).toInstant())
+ .destination(rs.getString("destination"))
+ .additionalInfo(rs.getString("additionalInfo"))
+ .build();
+ }
+
+ private Set<Vehicle> getVehiclesFromOperationId(long operationId) throws PersistenceException {
+ String sql = "SELECT vehicleId FROM VehicleOperation WHERE operationId = ?";
+ Set<Vehicle> vehicles = new HashSet<>();
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, operationId);
+ pstmt.execute();
+
+ try (ResultSet rs = pstmt.getResultSet()) {
+ while (rs.next()) {
+ vehicles.add(vehicleDAO.get(rs.getLong("vehicleId")));
+ }
+ }
+ }
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ } catch (ElementNotFoundException e) {
+ throw new PersistenceException("VehicleOperation contained nonexistent vehicle", e);
+ }
+
+ return vehicles;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDAO.java
new file mode 100644
index 0000000..02e742c
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDAO.java
@@ -0,0 +1,28 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import java.util.Set;
+
+public interface RegistrationDAO {
+
+ /**
+ * Persist the given registration.
+ *
+ * @param vehicleId the id of the target vehicle
+ * @param registrations that should be stored
+ * @return a list of the ids that were assigned
+ * @throws PersistenceException if the registration could not be persisted
+ */
+ Set<Long> add(long vehicleId, Set<Registration> registrations) throws PersistenceException;
+
+ /**
+ * Make registration with the given id inactive.
+ *
+ * @param id of the registration that should be made inactive
+ * @throws ElementNotFoundException if no registration with the given id exists
+ * @throws PersistenceException if the registration could not be made inactive
+ */
+ void remove(long id) throws ElementNotFoundException, PersistenceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDatabaseDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDatabaseDAO.java
new file mode 100644
index 0000000..1006a33
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/RegistrationDatabaseDAO.java
@@ -0,0 +1,130 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class RegistrationDatabaseDAO implements RegistrationDAO {
+
+ private JDBCConnectionManager jdbcConnectionManager;
+ private EmployeeDAO employeePersistence;
+
+ @Autowired
+ public RegistrationDatabaseDAO(
+ JDBCConnectionManager jdbcConnectionManager, EmployeeDAO employeePersistence) {
+ this.jdbcConnectionManager = jdbcConnectionManager;
+ this.employeePersistence = employeePersistence;
+ }
+
+ @Override
+ public Set<Long> add(long vehicleId, Set<Registration> registrations)
+ throws PersistenceException {
+ String sql =
+ "INSERT INTO Registration (vehicleId, employeeId, start, end, active) VALUES (?,?,?,?,?)";
+ String sql2 = "UPDATE Vehicle SET status = 'FREI_WACHE' WHERE id = ?;";
+
+ Set<Long> vehicleIds = new HashSet<>();
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+
+ try (PreparedStatement pstmt =
+ con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setLong(1, vehicleId);
+
+ for (Registration r : registrations) {
+ pstmt.setLong(2, r.employee().id());
+ pstmt.setObject(3, OffsetDateTime.ofInstant(r.start(), ZoneId.systemDefault()));
+ pstmt.setObject(4, OffsetDateTime.ofInstant(r.end(), ZoneId.systemDefault()));
+ pstmt.setBoolean(5, true);
+ pstmt.addBatch();
+ }
+
+ pstmt.executeBatch();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ while (rs.next()) vehicleIds.add(rs.getLong(1));
+ }
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql2)) {
+ pstmt.setLong(1, vehicleId);
+ if (pstmt.executeUpdate() != 1) {
+ con.rollback();
+ throw new PersistenceException("Failed to persist registration");
+ }
+ }
+
+ con.commit();
+ return vehicleIds;
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void remove(long id) throws ElementNotFoundException, PersistenceException {
+ throw new UnsupportedOperationException();
+ }
+
+ protected List<Registration> list(long vehicleId) throws PersistenceException {
+ String sql = "SELECT * FROM Registration WHERE vehicleId = ?";
+
+ List<Registration> registrationList = new ArrayList<>();
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, vehicleId);
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ while (rs.next()) {
+ long employeeId = rs.getLong("employeeId");
+ // TODO: replace the following with employeePersistence.get once implemented
+ Employee emp =
+ employeePersistence
+ .list()
+ .stream()
+ .filter(employee -> employee.id() == employeeId)
+ .findAny()
+ .orElse(null);
+ Registration registration =
+ Registration.builder()
+ .id(rs.getLong("id"))
+ .start(
+ (rs.getObject("start", OffsetDateTime.class))
+ .toInstant())
+ .end(
+ (rs.getObject("end", OffsetDateTime.class))
+ .toInstant())
+ .employee(emp)
+ .build();
+ registrationList.add(registration);
+ }
+ }
+ }
+
+ return registrationList;
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDAO.java
new file mode 100644
index 0000000..ed24498
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDAO.java
@@ -0,0 +1,54 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import java.util.Set;
+
+public interface VehicleDAO {
+
+ /**
+ * Persist the given vehicle.
+ *
+ * @param vehicle that should be stored
+ * @return the id that was assigned
+ * @throws PersistenceException if the vehicle could not be persisted
+ */
+ long add(Vehicle vehicle) throws PersistenceException;
+
+ /**
+ * Update the given vehicle.
+ *
+ * @param vehicle that should be updated
+ * @throws ElementNotFoundException if no vehicle with the given id exists
+ * @throws PersistenceException if the vehicle could not be updated
+ */
+ void update(Vehicle vehicle) throws ElementNotFoundException, PersistenceException;
+
+ /**
+ * Get all stored vehicles.
+ *
+ * @return list containing all stored vehicles
+ * @throws PersistenceException if loading the stored vehicles failed
+ */
+ Set<Vehicle> list() throws PersistenceException;
+
+ /**
+ * Returns the vehicle with the given id.
+ *
+ * @param vehicleId id of the vehicle that should be returned
+ * @return vehicle with the given id
+ * @throws ElementNotFoundException if no vehicle with the given id exists
+ * @throws PersistenceException if the vehicle could not be loaded
+ */
+ Vehicle get(long vehicleId) throws ElementNotFoundException, PersistenceException;
+
+ /**
+ * Remove vehicle with the given id from the store.
+ *
+ * @param id of the vehicle that should be removed
+ * @throws ElementNotFoundException if no vehicle with the given id exists
+ * @throws PersistenceException if the vehicle could not be removed
+ */
+ void remove(long id) throws ElementNotFoundException, PersistenceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDatabaseDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDatabaseDAO.java
new file mode 100644
index 0000000..dd7c0f2
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dao/VehicleDatabaseDAO.java
@@ -0,0 +1,211 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.ConstructionType;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.VehicleType;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class VehicleDatabaseDAO implements VehicleDAO {
+
+ private final JDBCConnectionManager jdbcConnectionManager;
+ private RegistrationDatabaseDAO registrationDatabaseDao;
+
+ public VehicleDatabaseDAO(
+ JDBCConnectionManager j, RegistrationDatabaseDAO registrationDatabaseDao) {
+ jdbcConnectionManager = j;
+ this.registrationDatabaseDao = registrationDatabaseDao;
+ }
+
+ @Override
+ public long add(Vehicle v) throws PersistenceException {
+ String sql =
+ "INSERT INTO VehicleVersion (name,hasNef,constructionType,type) VALUES (?,?,?,?)";
+ String sql2 = "INSERT INTO Vehicle (version,status) VALUES (?,?)";
+ String sql3 = "UPDATE VehicleVersion SET name=? WHERE id=?";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+ String name = "";
+ long version, id;
+
+ try (PreparedStatement pstmt =
+ con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setString(1, name);
+ pstmt.setBoolean(2, v.hasNef());
+ pstmt.setInt(3, v.constructionType().ordinal());
+ pstmt.setString(4, v.type().name());
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (!rs.next())
+ throw new PersistenceException("Failed to insert into VehicleVersion");
+
+ version = rs.getLong(1);
+ }
+ }
+
+ try (PreparedStatement pstmt =
+ con.prepareStatement(sql2, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setLong(1, version);
+ pstmt.setInt(2, Status.ABGEMELDET.ordinal());
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (!rs.next()) {
+ con.rollback();
+ throw new PersistenceException("Failed to insert into Vehicle");
+ }
+
+ id = rs.getLong(1);
+ }
+
+ name = v.type().name() + "-" + id;
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql3)) {
+ pstmt.setString(1, name);
+ pstmt.setLong(2, version);
+
+ if (pstmt.executeUpdate() != 1) {
+ con.rollback();
+ throw new PersistenceException("Failed to update VehicleVersion");
+ }
+ }
+
+ con.commit();
+ return id;
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void update(Vehicle v) throws ElementNotFoundException, PersistenceException {
+ String sql = "SELECT version FROM Vehicle WHERE id = ?";
+ String sql2 =
+ "MERGE INTO VehicleVersion(name, constructionType, type, hasNef)"
+ + " KEY(name, constructionType, type, hasNef) VALUES(?, ?, ?, ?)";
+ String sql3 = "UPDATE Vehicle SET version = ?, status = ? WHERE id = ?";
+
+ long versionId;
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ con.setAutoCommit(false);
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, v.id());
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ if (!rs.next()) throw new ElementNotFoundException("No such vehicleId exists");
+
+ versionId = rs.getLong(1);
+ }
+ }
+
+ try (PreparedStatement pstmt =
+ con.prepareStatement(sql2, Statement.RETURN_GENERATED_KEYS)) {
+ pstmt.setString(1, v.type().name() + "-" + v.id());
+ pstmt.setString(2, v.constructionType().name());
+ pstmt.setString(3, v.type().name());
+ pstmt.setBoolean(4, v.hasNef());
+ pstmt.executeUpdate();
+
+ try (ResultSet rs = pstmt.getGeneratedKeys()) {
+ if (rs.next()) {
+ // version changed, update it
+ versionId = rs.getLong(1);
+ }
+ }
+ }
+
+ try (PreparedStatement pstmt = con.prepareStatement(sql3)) {
+ pstmt.setLong(1, versionId);
+ pstmt.setString(2, v.status().name());
+ pstmt.setLong(3, v.id());
+ pstmt.executeUpdate();
+ }
+
+ con.commit();
+ } catch (SQLException e) {
+ jdbcConnectionManager.rollbackConnection();
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public Set<Vehicle> list() throws PersistenceException {
+ Set<Vehicle> result = new HashSet<>();
+
+ String sql =
+ "Select * from VehicleVersion, Vehicle where VehicleVersion.id=Vehicle.version";
+
+ try (PreparedStatement pstmt =
+ jdbcConnectionManager.getConnection().prepareStatement(sql)) {
+ pstmt.executeQuery();
+ try (ResultSet rs = pstmt.getResultSet()) {
+ while (rs.next()) {
+ result.add(vehicleFromRS(rs));
+ }
+ }
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ return result;
+ }
+
+ @Override
+ public Vehicle get(long id) throws ElementNotFoundException, PersistenceException {
+ String sql =
+ "SELECT a.id, b.name, b.constructionType, b.type, a.status, b.hasNef"
+ + " FROM Vehicle a"
+ + " INNER JOIN VehicleVersion b"
+ + " ON version = b.id"
+ + " WHERE a.id = ?";
+
+ try {
+ Connection con = jdbcConnectionManager.getConnection();
+ try (PreparedStatement pstmt = con.prepareStatement(sql)) {
+ pstmt.setLong(1, id);
+
+ try (ResultSet rs = pstmt.executeQuery()) {
+ if (!rs.first()) throw new ElementNotFoundException("No such vehicle exists");
+
+ return vehicleFromRS(rs);
+ }
+ }
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void remove(long id) throws ElementNotFoundException, PersistenceException {
+ throw new UnsupportedOperationException();
+ }
+
+ private Vehicle vehicleFromRS(ResultSet rs) throws SQLException, PersistenceException {
+ return Vehicle.builder()
+ .id(rs.getLong("Vehicle.id"))
+ .name(rs.getString("name"))
+ .constructionType(ConstructionType.values()[rs.getInt("constructionType")])
+ .type(VehicleType.valueOf(rs.getString("type")))
+ .status(Status.values()[rs.getInt("status")])
+ .hasNef(rs.getBoolean("hasNef"))
+ .registrations(registrationDatabaseDao.list(rs.getLong("id")))
+ .build();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Employee.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Employee.java
new file mode 100644
index 0000000..f45550e
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Employee.java
@@ -0,0 +1,51 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import com.google.auto.value.AutoValue;
+import java.time.LocalDate;
+
+@AutoValue
+public abstract class Employee {
+ public enum EducationLevel {
+ RS,
+ NFS,
+ NKV,
+ NKA,
+ NKI,
+ NA
+ }
+
+ public abstract long id();
+
+ public abstract String name();
+
+ public abstract LocalDate birthday();
+
+ public abstract EducationLevel educationLevel();
+
+ public abstract boolean isDriver();
+
+ public abstract boolean isPilot();
+
+ public static Builder builder() {
+ return new AutoValue_Employee.Builder().id(0);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder id(long id);
+
+ public abstract Builder name(String name);
+
+ public abstract Builder birthday(LocalDate birthday);
+
+ public abstract Builder educationLevel(EducationLevel educationLevel);
+
+ public abstract Builder isDriver(boolean isDriver);
+
+ public abstract Builder isPilot(boolean isPilot);
+
+ public abstract Employee build();
+ }
+
+ public abstract Builder toBuilder();
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/EmployeeValidator.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/EmployeeValidator.java
new file mode 100644
index 0000000..b03fa04
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/EmployeeValidator.java
@@ -0,0 +1,23 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException;
+
+public class EmployeeValidator {
+
+ public static boolean validate(Employee employee) throws InvalidEmployeeException {
+
+ if (employee.name() == null || employee.name().trim().length() == 0) {
+ throw new InvalidEmployeeException("Name darf nicht leer sein!");
+ }
+
+ if (employee.birthday() == null) {
+ throw new InvalidEmployeeException("Geburtsdatum darf nicht leer sein!");
+ }
+
+ if (employee.educationLevel() == null) {
+ throw new InvalidEmployeeException("Ausbildungsgrad darf nicht leer sein!");
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Operation.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Operation.java
new file mode 100644
index 0000000..e119622
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Operation.java
@@ -0,0 +1,70 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import com.google.auto.value.AutoValue;
+import java.time.Instant;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+@AutoValue
+public abstract class Operation {
+ public enum Severity {
+ A,
+ B,
+ C,
+ D,
+ E,
+ O,
+ }
+
+ public enum Status {
+ ACTIVE,
+ COMPLETED,
+ CANCELLED,
+ }
+
+ public abstract long id();
+
+ public abstract String opCode();
+
+ @Nullable
+ public abstract Severity severity();
+
+ public abstract Status status();
+
+ public abstract Set<Vehicle> vehicles();
+
+ @Nullable
+ public abstract Instant created();
+
+ public abstract String destination();
+
+ @Nullable
+ public abstract String additionalInfo();
+
+ public static Builder builder() {
+ return new AutoValue_Operation.Builder().id(0);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder id(long id);
+
+ public abstract Builder opCode(String opCode);
+
+ public abstract Builder severity(Severity severity);
+
+ public abstract Builder status(Status status);
+
+ public abstract Builder vehicles(Set<Vehicle> vehicles);
+
+ public abstract Builder created(Instant created);
+
+ public abstract Builder destination(String destination);
+
+ public abstract Builder additionalInfo(String additionalInfo);
+
+ public abstract Operation build();
+ }
+
+ public abstract Builder toBuilder();
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Registration.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Registration.java
new file mode 100644
index 0000000..a12c038
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Registration.java
@@ -0,0 +1,34 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import com.google.auto.value.AutoValue;
+import java.time.Instant;
+
+@AutoValue
+public abstract class Registration {
+ public abstract long id();
+
+ public abstract Instant start();
+
+ public abstract Instant end();
+
+ public abstract Employee employee();
+
+ public static Builder builder() {
+ return new AutoValue_Registration.Builder().id(0);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder id(long id);
+
+ public abstract Builder start(Instant start);
+
+ public abstract Builder end(Instant end);
+
+ public abstract Builder employee(Employee employee);
+
+ public abstract Registration build();
+ }
+
+ public abstract Builder toBuilder();
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/RegistrationValidator.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/RegistrationValidator.java
new file mode 100644
index 0000000..9512f64
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/RegistrationValidator.java
@@ -0,0 +1,174 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee.EducationLevel;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.VehicleType;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class RegistrationValidator {
+
+ private RegistrationValidator() {}
+
+ public static void validate(Vehicle vehicle, Set<Registration> registrations)
+ throws InvalidVehicleException, InvalidRegistrationException {
+ /*
+ Vehicles and Employees are assumed to be valid.
+ They have been checked at creation, and for them to be checked again, access to
+ VehicleValidator and EmployeeValidator are needed, which are not available at this time.
+ */
+ /*
+ The method used here goes as follows: All given employees are inspected in regards to their
+ qualifications, and added to the appropriate lists of the roles they could fill.
+ For example, an NFS, who is also a driver, would be added to the lists driverIds, nfsIds
+ and rsIds (because an NFS can always substitute an RS).
+ Afterwards, the number of people is checked according to the chosen vehicle type, and if
+ the number is okay, the program tries to find a valid combination of roles for the vehicle.
+ For example, for an RTW, first a driver is chosen, their ID marked as found in the aptly
+ titled HashMap, and then for the second RS, the list of RS is checked, excluding the chosen
+ driver. If no other valid RS is found, the next possible driver is chosen, and so on. If no
+ valid combination is found, an InvalidRegistrationException is thrown.
+ */
+ List<Long> pilotIds = new LinkedList<>();
+ List<Long> driverIds = new LinkedList<>();
+ List<Long> naIds = new LinkedList<>();
+ List<Long> nfsIds = new LinkedList<>();
+ List<Long> rsIds = new LinkedList<>();
+ HashMap<Long, Boolean> found =
+ new HashMap<>(); // needed later in DFS, checks that no person is chosen twice
+ int total = 0;
+ for (Registration registration : registrations) {
+ total++;
+ if (found.put(registration.employee().id(), false) != null) {
+ throw new InvalidRegistrationException(
+ "Person mit der ID: "
+ + registration.employee().id()
+ + " wurde mehrmals hinzugefügt!");
+ }
+ if (registration.employee().isPilot()) {
+ pilotIds.add(registration.employee().id());
+ }
+ if (registration.employee().isDriver()) {
+ driverIds.add(registration.employee().id());
+ }
+ if (registration.employee().educationLevel() == EducationLevel.NA) {
+ naIds.add(registration.employee().id());
+ nfsIds.add(registration.employee().id());
+ rsIds.add(registration.employee().id());
+ } else if (isNFS(registration.employee())) {
+ nfsIds.add(registration.employee().id());
+ rsIds.add(registration.employee().id());
+ } else { // only RS left
+ rsIds.add(registration.employee().id());
+ }
+ }
+ if (total <= 0) {
+ throw new InvalidRegistrationException("Kein Personal ausgewählt!");
+ }
+ if (vehicle.type() == VehicleType.NAH) {
+ /*
+ NAH
+ 1 Pilot
+ 1 NFS
+ 1 NA
+ 3-4 Personen
+ */
+ if (total < 3) {
+ throw new InvalidRegistrationException("Zu wenig Personal für NAH!");
+ } else if (total > 4) {
+ throw new InvalidRegistrationException("Zu viel Personal für NAH!");
+ }
+ for (long pilot_id : pilotIds) {
+ found.put(pilot_id, true);
+ for (long na_id : naIds) {
+ if (found.get(na_id)) continue;
+ found.put(na_id, true);
+ for (long nfs_id : nfsIds) {
+ if (found.get(nfs_id)) continue;
+ return;
+ }
+ found.put(na_id, false);
+ }
+ found.put(pilot_id, false);
+ }
+ throw new InvalidRegistrationException(
+ "Keine gültige Kombination von Personen für NAH!");
+ } else if (vehicle.type() == VehicleType.NEF) {
+ /*
+ NEF
+ 1 Driver (has to be NFS)
+ 1 NA
+ */
+ if (total < 2) {
+ throw new InvalidRegistrationException("Zu wenig Personal für NEF!");
+ } else if (total > 3) {
+ throw new InvalidRegistrationException("Zu viel Personal für NEF!");
+ }
+ for (long driver_id : driverIds) {
+ if (!nfsIds.contains(driver_id))
+ continue; // if possible driver is not NFS, skip him
+ found.put(driver_id, true);
+ for (long na_id : naIds) {
+ if (found.get(na_id)) continue;
+ return;
+ }
+ found.put(driver_id, false);
+ }
+ throw new InvalidRegistrationException(
+ "Keine gültige Kombination von Personen für NEF!");
+ } else if (vehicle.type() == VehicleType.BKTW) {
+ /*
+ BKTW
+ 1 Driver
+ */
+ if (total > 3) {
+ throw new InvalidRegistrationException("Zu viel Personal für BKTW!");
+ }
+ if (!driverIds.isEmpty()) {
+ return;
+ }
+ throw new InvalidRegistrationException("Kein Fahrer gefunden für BKTW!");
+ } else { // KTW or RTW, both have the same requirements
+ /*
+ RTW/KTW
+ 1 Driver
+ 1 RS
+ */
+ if (total < 2) {
+ throw new InvalidRegistrationException(
+ "Zu wenig Personal für " + vehicle.type().name() + "!");
+ } else if (total > 4) {
+ throw new InvalidRegistrationException(
+ "Zu viel Persoanl für " + vehicle.type().name() + "!");
+ }
+ for (long driver_id : driverIds) { // driver includes rs
+ found.put(driver_id, true);
+ for (long rs_id : rsIds) {
+ if (found.get(rs_id)) continue;
+ return;
+ }
+ }
+ throw new InvalidRegistrationException(
+ "Keine gültige Kombination von Personen für " + vehicle.type().name() + "!");
+ }
+ }
+
+ private static boolean isNFS(Employee employee) {
+ EducationLevel educationLevel = employee.educationLevel();
+ switch (educationLevel) {
+ case NFS:
+ return true;
+ case NKA:
+ return true;
+ case NKI:
+ return true;
+ case NKV:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Vehicle.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Vehicle.java
new file mode 100644
index 0000000..c2033f5
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/dto/Vehicle.java
@@ -0,0 +1,73 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto;
+
+import com.google.auto.value.AutoValue;
+import java.util.List;
+import javax.annotation.Nullable;
+
+@AutoValue
+public abstract class Vehicle {
+ public enum ConstructionType {
+ NORMAL,
+ HOCHDACH,
+ MITTELHOCHDACH,
+ }
+
+ public enum VehicleType {
+ BKTW,
+ KTW_B,
+ KTW,
+ RTW,
+ NEF,
+ NAH,
+ }
+
+ public enum Status {
+ ABGEMELDET,
+ FREI_WACHE,
+ FREI_FUNK,
+ ZUM_BERUFUNGSORT,
+ AM_BERUFUNGSORT,
+ ZUM_ZIELORT,
+ AM_ZIELORT,
+ }
+
+ public abstract long id();
+
+ public abstract String name();
+
+ public abstract ConstructionType constructionType();
+
+ public abstract VehicleType type();
+
+ public abstract Status status();
+
+ public abstract boolean hasNef();
+
+ @Nullable
+ public abstract List<Registration> registrations();
+
+ public static Builder builder() {
+ return new AutoValue_Vehicle.Builder().id(0);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder id(long id);
+
+ public abstract Builder name(String name);
+
+ public abstract Builder constructionType(ConstructionType constructionType);
+
+ public abstract Builder type(VehicleType type);
+
+ public abstract Builder status(Status status);
+
+ public abstract Builder hasNef(boolean hasNef);
+
+ public abstract Builder registrations(List<Registration> registrations);
+
+ public abstract Vehicle build();
+ }
+
+ public abstract Builder toBuilder();
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeService.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeService.java
new file mode 100644
index 0000000..fa4d0e6
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeService.java
@@ -0,0 +1,46 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.Set;
+
+public interface EmployeeService {
+
+ /**
+ * Add given employee to the store.
+ *
+ * @param employee that should be added to the store
+ * @return the id that was assigned
+ * @throws InvalidEmployeeException if the employee is invalid
+ * @throws ServiceException if the employee could not be persisted
+ */
+ long add(Employee employee) throws InvalidEmployeeException, ServiceException;
+
+ /**
+ * Update the given employee.
+ *
+ * @param employee that should be updated
+ * @return the updated employee
+ * @throws InvalidEmployeeException if the employee is invalid
+ * @throws ServiceException if the updated employee could not be persisted
+ */
+ Employee update(Employee employee) throws InvalidEmployeeException, ServiceException;
+
+ /**
+ * Get all stored employees.
+ *
+ * @return list containing all stored employees
+ * @throws ServiceException if loading the stored employees failed
+ */
+ Set<Employee> list() throws ServiceException;
+
+ /**
+ * Remove employee with the given id from the store.
+ *
+ * @param id of the employee that should be removed
+ * @throws InvalidEmployeeException if given employee id is invalid or does not exist
+ * @throws ServiceException if the employee could not be removed from the store
+ */
+ void remove(long id) throws InvalidEmployeeException, ServiceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeServiceImpl.java
new file mode 100644
index 0000000..945fb49
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/EmployeeServiceImpl.java
@@ -0,0 +1,59 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.EmployeeDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Employee;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.EmployeeValidator;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.Set;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EmployeeServiceImpl implements EmployeeService {
+
+ private final EmployeeDAO employeePersistence;
+
+ public EmployeeServiceImpl(EmployeeDAO employeePersistence) {
+ this.employeePersistence = employeePersistence;
+ }
+
+ @Override
+ public long add(Employee employee) throws InvalidEmployeeException, ServiceException {
+
+ EmployeeValidator.validate(employee);
+ try {
+ return employeePersistence.add(employee);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public Employee update(Employee employee) throws InvalidEmployeeException, ServiceException {
+
+ EmployeeValidator.validate(employee);
+ try {
+ employeePersistence.update(employee);
+ return employee;
+ } catch (ElementNotFoundException | PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public Set<Employee> list() throws ServiceException {
+
+ try {
+ return employeePersistence.list();
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public void remove(long id) throws InvalidEmployeeException, ServiceException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationService.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationService.java
new file mode 100644
index 0000000..ca1dce9
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationService.java
@@ -0,0 +1,70 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.SortedSet;
+
+public interface OperationService {
+
+ /**
+ * Add given operation to the store.
+ *
+ * @param operation that should be added to the store
+ * @return the id that was assigned
+ * @throws InvalidOperationException if the operation is invalid
+ * @throws ServiceException if the operation could not be persisted
+ */
+ long add(Operation operation) throws InvalidOperationException, ServiceException;
+
+ /**
+ * Request new vehicles to the given operation.
+ *
+ * @param operationId id of the operation that the vehicles should be send to
+ * @param vehicleIds the ids of the vehicles that should be send to the given operation
+ * @throws InvalidOperationException if the operationId is invalid or does not exist
+ * @throws InvalidVehicleException if one of the vehicle ids is invalid or does not exist
+ * @throws ServiceException if the vehicles could not be loaded or the operation could not be
+ * persisted
+ */
+ void requestVehicles(long operationId, Set<Long> vehicleIds)
+ throws InvalidOperationException, InvalidVehicleException, ServiceException;
+
+ /**
+ * Completes the given operation with the specified status.
+ *
+ * @param operationId id of the operation that should be completed
+ * @param status of the completed operation, either {@link Status#COMPLETED} or {@link
+ * Status#CANCELLED}
+ * @throws InvalidOperationException if the operationId is invalid or does not exist
+ * @throws ServiceException if the operation could not be persisted
+ */
+ void complete(long operationId, Status status)
+ throws InvalidOperationException, ServiceException;
+
+ /**
+ * Get all available vehicles, sorted by how well they fit to the given opCode, starting with
+ * the best fitting.
+ *
+ * @param opCode the operation code that is used to determine the ranking
+ * @return a sorted list containing all available vehicles
+ * @throws InvalidOperationException if the opCode is invalid
+ * @throws ServiceException if loading the stored vehicles failed
+ */
+ SortedSet<Vehicle> rankVehicles(String opCode)
+ throws InvalidOperationException, ServiceException;
+
+ /**
+ * Get all stored operations with matching status.
+ *
+ * @param statuses set containing all statuses that should be matched
+ * @return list containing all matched operations
+ * @throws ServiceException if loading the stored operations failed
+ */
+ Set<Operation> list(EnumSet<Status> statuses) throws ServiceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java
new file mode 100644
index 0000000..baae598
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/OperationServiceImpl.java
@@ -0,0 +1,286 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.OperationDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.VehicleDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Severity;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Operation.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.VehicleType;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OperationServiceImpl implements OperationService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OperationServiceImpl.class);
+
+ private final OperationDAO operationDAO;
+ private final VehicleDAO vehicleDAO;
+ private final VehicleService vehicleService;
+
+ public OperationServiceImpl(
+ OperationDAO operationDAO, VehicleDAO vehicleDAO, VehicleService vehicleService) {
+ this.operationDAO = operationDAO;
+ this.vehicleDAO = vehicleDAO;
+ this.vehicleService = vehicleService;
+ }
+
+ @Override
+ public long add(Operation o) throws InvalidOperationException, ServiceException {
+ if (o.created() != null) {
+ throw new InvalidOperationException("Erstellungszeitpunkt darf nicht gesetzt sein");
+ }
+
+ if (o.severity() != null) {
+ throw new InvalidOperationException("Der Schweregrad darf nicht gesetzt sein");
+ }
+
+ if (o.id() != 0) {
+ throw new InvalidOperationException("Einsatz-ID muss 0 sein");
+ }
+
+ if (o.status() != Status.ACTIVE)
+ LOG.info("Status was set but will be overridden"); // TODO: nullable instead??
+
+ try {
+ for (long id : (Iterable<Long>) o.vehicles().stream().map(Vehicle::id)::iterator) {
+ Vehicle v = vehicleDAO.get(id);
+ VehicleServiceImpl.validateVehicle(v);
+
+ if (v.status() != Vehicle.Status.FREI_FUNK
+ && v.status() != Vehicle.Status.FREI_WACHE)
+ throw new InvalidOperationException("Fahrzeug nicht verfügbar: " + v.status());
+ }
+
+ validateOperation(o);
+
+ return operationDAO.add(
+ o.toBuilder()
+ .created(Instant.now())
+ .severity(extractSeverityFromOpCode(o.opCode()))
+ .status(Status.ACTIVE)
+ .build());
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ } catch (InvalidVehicleException e) {
+ // already logged as invalid vehicle
+ throw new InvalidOperationException("Enthaltenes Fahrzeug ist ungültig", e);
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException("Enthaltenes Fahrzeug existiert nicht", e);
+ }
+ }
+
+ @Override
+ public void requestVehicles(long operationId, Set<Long> vehicleIds)
+ throws InvalidOperationException, InvalidVehicleException, ServiceException {
+ Set<Vehicle> vs = new HashSet<>();
+
+ try {
+ if (operationId <= 0) {
+ throw new InvalidOperationException("Einsatz-ID ist ungültig");
+ }
+ Operation o = operationDAO.get(operationId);
+ validateOperation(o);
+
+ if (o.opCode().trim().isEmpty()
+ || extractSeverityFromOpCode(o.opCode()) != o.severity()) {
+ throw new InvalidOperationException("Einsatzcode ist ungültig");
+ }
+
+ if (o.status() != Status.ACTIVE) {
+ throw new InvalidOperationException("Einsatz ist ungültig");
+ }
+
+ if (o.created() == null) {
+ throw new InvalidOperationException("Erstellungszeitpunkt darf nicht leer sein");
+ }
+
+ for (Long id : vehicleIds) {
+ if (id <= 0) {
+ throw new InvalidVehicleException("Fahrzeug-ID ist ungültig");
+ }
+
+ try {
+ Vehicle v = vehicleDAO.get(id);
+ VehicleServiceImpl.validateVehicle(v);
+ if (v.status() != Vehicle.Status.FREI_FUNK
+ && v.status() != Vehicle.Status.FREI_WACHE)
+ throw new InvalidOperationException(
+ "Fahrzeug nicht verfügbar: " + v.status());
+
+ vs.add(v);
+ } catch (ElementNotFoundException e) {
+ throw new InvalidVehicleException("VehicleId ist invalid");
+ }
+ }
+
+ vs.addAll(o.vehicles());
+ if (vs.equals(o.vehicles())) return;
+
+ operationDAO.update(o.toBuilder().vehicles(vs).build());
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException("Kein Einsatz mit dieser ID existiert");
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public void complete(long operationId, Status status)
+ throws InvalidOperationException, ServiceException {
+ try {
+ Operation o = operationDAO.get(operationId);
+ operationDAO.update(o.toBuilder().status(status).build());
+ } catch (ElementNotFoundException e) {
+ throw new InvalidOperationException(e);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ }
+
+ @Override
+ public SortedSet<Vehicle> rankVehicles(String opCode)
+ throws InvalidOperationException, ServiceException {
+ Set<Vehicle> vehicles =
+ vehicleService.list(EnumSet.complementOf(EnumSet.of(Vehicle.Status.ABGEMELDET)));
+
+ List<Predicate<Vehicle>> priorities = new ArrayList<>();
+ Predicate<Vehicle> ktw = v -> v.type() == VehicleType.KTW;
+ Predicate<Vehicle> rtwNoNEF = v -> v.type() == VehicleType.RTW && !v.hasNef();
+ Predicate<Vehicle> rtwNEF = v -> v.type() == VehicleType.RTW && v.hasNef();
+ Predicate<Vehicle> nef = v -> v.type() == VehicleType.NEF;
+ Predicate<Vehicle> nah = v -> v.type() == VehicleType.NAH;
+
+ switch (extractSeverityFromOpCode(opCode)) {
+ case A:
+ // fallthrough
+ case B:
+ // fallthrough
+ case O:
+ priorities.add(ktw);
+ priorities.add(rtwNoNEF);
+ priorities.add(rtwNEF);
+ break;
+ case C:
+ priorities.add(rtwNEF);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ case D:
+ priorities.add(rtwNEF);
+ priorities.add(nef);
+ priorities.add(nah);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ case E:
+ priorities.add(nah);
+ priorities.add(nef);
+ priorities.add(rtwNEF);
+ priorities.add(rtwNoNEF);
+ priorities.add(ktw);
+ break;
+ }
+
+ Comparator<Vehicle> vehicleComparator =
+ (v1, v2) -> {
+ for (Predicate<Vehicle> priority : priorities) {
+ if (priority.test(v1)) {
+ return -1;
+ }
+ if (priority.test(v2)) {
+ return +1;
+ }
+ }
+ return 0;
+ };
+
+ Supplier<TreeSet<Vehicle>> supplier = () -> new TreeSet<>(vehicleComparator);
+
+ return vehicles.stream().collect(Collectors.toCollection(supplier));
+ }
+
+ @Override
+ public Set<Operation> list(EnumSet<Status> statuses) throws ServiceException {
+ try {
+ Set<Operation> operations = operationDAO.list(statuses);
+ for (Operation o : operations) validateOperation(o);
+
+ return operations;
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ } catch (InvalidOperationException e) {
+ // database returned invalid values
+ throw new ServiceException("DB returned invalid operation", e);
+ }
+ }
+
+ private static void validateOperation(Operation o) throws InvalidOperationException {
+ if (o.vehicles().isEmpty()) {
+ throw new InvalidOperationException(
+ "Es muss mindestens ein Fahrzeug ausgewählt werden!");
+ }
+
+ for (Vehicle v : o.vehicles()) {
+ try {
+ VehicleServiceImpl.validateVehicle(v);
+ } catch (InvalidVehicleException e) {
+ throw new InvalidOperationException("Fahrzeug " + v.name() + " ist ungültig", e);
+ }
+
+ // TODO: validate if NEF/RTW/NAH conditions?
+ }
+
+ Instant created = o.created();
+ if (created != null && created.isAfter(Instant.now())) {
+ throw new InvalidOperationException("Einsatz wurde in der Zukunft erstellt");
+ }
+
+ if (o.destination() == null || o.destination().trim().isEmpty()) {
+ throw new InvalidOperationException("Adresse darf nicht leer sein");
+ }
+
+ if (o.destination().length() > 100) {
+ throw new InvalidOperationException("Adresse darf 100 Zeichen nicht überschreiten");
+ }
+
+ if (o.additionalInfo() != null && o.additionalInfo().length() > 100) {
+ throw new InvalidOperationException("Anmerkung darf 100 Zeichen nicht überschreiten");
+ }
+ }
+
+ private static final Pattern opCodePattern =
+ Pattern.compile("(?:\\w{1,3}-\\d{0,2})([ABCDEO])(?:.*)");
+
+ private static Severity extractSeverityFromOpCode(String opCode)
+ throws InvalidOperationException {
+ Matcher m = opCodePattern.matcher(opCode);
+
+ if (!m.matches()) {
+ throw new InvalidOperationException("Einsatzcode ist ungültig");
+ }
+
+ return Severity.valueOf(m.group(1));
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationService.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationService.java
new file mode 100644
index 0000000..027417f
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationService.java
@@ -0,0 +1,32 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.Set;
+
+public interface RegistrationService {
+
+ /**
+ * Register employee to a vehicle.
+ *
+ * @param vehicleId the id of the target vehicle
+ * @param registrations that should be added to the vehicle
+ * @return the list of ids that were assigned
+ * @throws InvalidVehicleException if the vehicleId is invalid or does not exist
+ * @throws InvalidRegistrationException if the registration is invalid
+ * @throws ServiceException if the registration could not be persisted
+ */
+ Set<Long> add(long vehicleId, Set<Registration> registrations)
+ throws InvalidVehicleException, InvalidRegistrationException, ServiceException;
+
+ /**
+ * Remove given registration from the store.
+ *
+ * @param registrationId the id of the registration that should be removed
+ * @throws InvalidRegistrationException if the registration is invalid or does not exist
+ * @throws ServiceException if the registration could not be removed from the store
+ */
+ void remove(long registrationId) throws InvalidRegistrationException, ServiceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationServiceImpl.java
new file mode 100644
index 0000000..3ec69e7
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/RegistrationServiceImpl.java
@@ -0,0 +1,52 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.RegistrationDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.VehicleDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Registration;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.RegistrationValidator;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RegistrationServiceImpl implements RegistrationService {
+
+ private final RegistrationDAO registrationDAO;
+ private final VehicleDAO vehicleDAO;
+
+ @Autowired
+ public RegistrationServiceImpl(RegistrationDAO registrationDAO, VehicleDAO vehicleDAO) {
+ this.registrationDAO = registrationDAO;
+ this.vehicleDAO = vehicleDAO;
+ }
+
+ @Override
+ public Set<Long> add(long vehicleId, Set<Registration> registrations)
+ throws InvalidVehicleException, InvalidRegistrationException, ServiceException {
+
+ if (vehicleId <= 0) throw new InvalidVehicleException("VehicleId invalid");
+
+ try {
+ Vehicle vehicle = vehicleDAO.get(vehicleId);
+
+ RegistrationValidator.validate(vehicle, registrations);
+
+ return registrationDAO.add(vehicle.id(), registrations);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ } catch (ElementNotFoundException e) {
+ throw new InvalidVehicleException(e);
+ }
+ }
+
+ @Override
+ public void remove(long registrationId) throws InvalidRegistrationException, ServiceException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleService.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleService.java
new file mode 100644
index 0000000..d96dfb7
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleService.java
@@ -0,0 +1,49 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.EnumSet;
+import java.util.Set;
+
+public interface VehicleService {
+
+ /**
+ * Add given vehicle to the store.
+ *
+ * @param vehicle that should be added to the store
+ * @return the id that was assigned
+ * @throws InvalidVehicleException if the vehicle is invalid
+ * @throws ServiceException if the vehicle could not be persisted
+ */
+ long add(Vehicle vehicle) throws InvalidVehicleException, ServiceException;
+
+ /**
+ * Update the given vehicle.
+ *
+ * @param vehicle that should be updated
+ * @return the updated vehicle
+ * @throws InvalidVehicleException if the vehicle is invalid
+ * @throws ServiceException if the updated vehicle could not be persisted
+ */
+ Vehicle update(Vehicle vehicle) throws InvalidVehicleException, ServiceException;
+
+ /**
+ * Get all stored vehicles with matching status.
+ *
+ * @param statuses set containing all statuses that should be matched
+ * @return list containing all stored vehicles
+ * @throws ServiceException if loading the stored vehicles failed
+ */
+ Set<Vehicle> list(EnumSet<Status> statuses) throws ServiceException;
+
+ /**
+ * Remove vehicle with the given id from the store.
+ *
+ * @param id of the vehicle that should be removed
+ * @throws InvalidVehicleException if given vehicle id is invalid or does not exist
+ * @throws ServiceException if the vehicle could not be removed from the store
+ */
+ void remove(long id) throws InvalidVehicleException, ServiceException;
+}
diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleServiceImpl.java
new file mode 100644
index 0000000..6a035c6
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/missioncontrol/service/VehicleServiceImpl.java
@@ -0,0 +1,116 @@
+package at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.service;
+
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dao.VehicleDAO;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.ConstructionType;
+import at.ac.tuwien.sepm.assignment.groupphase.missioncontrol.dto.Vehicle.Status;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException;
+import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+@Service
+public class VehicleServiceImpl implements VehicleService {
+
+ private VehicleDAO vehiclePersistence;
+
+ public VehicleServiceImpl(VehicleDAO vehiclePersistence) {
+ this.vehiclePersistence = vehiclePersistence;
+ }
+
+ public long add(Vehicle vehicle) throws InvalidVehicleException, ServiceException {
+ if (!CollectionUtils.isEmpty(vehicle.registrations())) {
+ throw new InvalidVehicleException(
+ "Fahrzeug kann nicht mit Anmeldungen erstellt werden");
+ }
+
+ validateVehicle(vehicle);
+ try {
+ vehiclePersistence.add(vehicle);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ return 0;
+ }
+
+ public Vehicle update(Vehicle vehicle) throws InvalidVehicleException, ServiceException {
+ validateVehicle(vehicle);
+ try {
+ vehiclePersistence.update(vehicle);
+ } catch (ElementNotFoundException e) {
+ throw new ServiceException("Element not found", e);
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+ return vehicle;
+ }
+
+ protected static void validateVehicle(Vehicle vehicle) throws InvalidVehicleException {
+ switch (vehicle.type()) {
+ case RTW:
+ if (vehicle.constructionType() == ConstructionType.NORMAL) {
+ throw new InvalidVehicleException("RTW darf kein Normales Dach haben");
+ } else if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) {
+ throw new InvalidVehicleException("RTW darf kein Mittelhochdach haben");
+ }
+ break;
+ case KTW:
+ if (vehicle.constructionType() == ConstructionType.NORMAL) {
+ throw new InvalidVehicleException("KTW darf kein Normales Dach haben");
+ }
+ break;
+ case KTW_B:
+ if (vehicle.constructionType() == ConstructionType.NORMAL) {
+ throw new InvalidVehicleException("KTW-B darf kein Normales Dach haben");
+ }
+ break;
+ case NEF:
+ if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) {
+ throw new InvalidVehicleException("NEF darf kein Mittelhochdach haben");
+ } else if (vehicle.constructionType() == ConstructionType.HOCHDACH) {
+ throw new InvalidVehicleException("NEF darf kein Hochdach haben");
+ }
+ break;
+ case NAH:
+ if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) {
+ throw new InvalidVehicleException("NEF darf kein Mittelhochdach haben");
+ } else if (vehicle.constructionType() == ConstructionType.HOCHDACH) {
+ throw new InvalidVehicleException("NEF darf kein Hochdach haben");
+ }
+ break;
+ case BKTW:
+ break;
+ default:
+ throw new IllegalStateException("BUG: invalid vehicle type" + vehicle.type());
+ }
+ }
+
+ @Override
+ public Set<Vehicle> list(EnumSet<Status> statuses) throws ServiceException {
+ if (statuses == null) {
+ throw new ServiceException("Statuses may not be null");
+ }
+
+ Set<Vehicle> vehicles;
+
+ try {
+ vehicles = vehiclePersistence.list();
+ } catch (PersistenceException e) {
+ throw new ServiceException(e);
+ }
+
+ return vehicles.stream()
+ .filter(vehicle -> statuses.contains(vehicle.status()))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public void remove(long id) throws InvalidVehicleException, ServiceException {
+ throw new UnsupportedOperationException();
+ }
+}