--- /dev/null
+package de.diejungsvondertanke.tankstelle;
+
+import de.diejungsvondertanke.tankstelle.error.NoSuchFuelTypeError;
+
+import javax.swing.*;
++import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.util.List;
+
+/**
+ * GUI for fuel stations
+ *
+ * @author Sergej Pavlenko
+ */
+public class FuelStationUI extends JFrame {
+
+ private JComboBox<String> comboFuelStations;
+ private JComboBox<FuelType> comboFuelTypes;
+ private JTextArea outputArea;
+ private JPanel contentPane;
+ private JList<String> listFuelStations;
++ private JTable tableFuelTypesPerStation;
++ private javax.swing.table.DefaultTableModel fuelTableModel;
+
+ public FuelStationUI() {
+ setContentPane(contentPane);
+ setTitle("Fuel Station Management System");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(800, 600);
+ setLocationRelativeTo(null);
+
+ initComponents();
+ }
+
+ /**
+ * Layout for GUI
+ *
+ * @author Sergej Pavlenko
+ */
+ private void initComponents() {
+ setLayout(new BorderLayout());
+
+ JPanel selectionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+ comboFuelStations = new JComboBox<>(buildStationNames());
+ comboFuelTypes = new JComboBox<>(FuelType.values());
+
+ listFuelStations = new JList<>();
+ listFuelStations.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ refreshStationNames();
+
+ selectionPanel.add(new JLabel("Fuel station:"));
+ selectionPanel.add(comboFuelStations);
+ selectionPanel.add(new JLabel("Fuel type:"));
+ selectionPanel.add(comboFuelTypes);
+
+ add(selectionPanel, BorderLayout.NORTH);
+
+ JTabbedPane tabbedPane = new JTabbedPane();
+ tabbedPane.addTab("Result", createResultPanel());
+ tabbedPane.addTab("Change price", createPricePanel());
+ tabbedPane.addTab("Change stock", createStockPanel());
+ tabbedPane.addTab("Search", createSearchPanel());
+ tabbedPane.addTab("New fuel station", createNewFuelStationPanel());
++ tabbedPane.addTab("Overview", createOverviewPanel());
+
+ add(tabbedPane, BorderLayout.CENTER);
+
+ outputArea = new JTextArea();
+ outputArea.setEditable(false);
+ outputArea.setLineWrap(true);
+ outputArea.setWrapStyleWord(true);
+ JScrollPane scrollPane = new JScrollPane(outputArea);
+ scrollPane.setPreferredSize(new Dimension(800, 150));
+ add(scrollPane, BorderLayout.SOUTH);
+ }
+
+ /**
+ * Creates names for the fuel stations
+ *
+ * @return fuel station names
+ *
+ * @author Sergej Pavlenko
+ */
+ private String[] buildStationNames() {
+ List<FuelStation> stations = Main.fuelStations;
+ String[] names = new String[stations.size()];
+ for (int i = 0; i < stations.size(); i++) {
+ FuelStation fs = stations.get(i);
+ String typeName = fs.getClass().getSimpleName();
+ names[i] = "Station " + (i + 1) + " (" + typeName + ")";
+ }
+ return names;
+ }
+
+ /**
+ * Getter method
+ *
+ * @return selected fuel station
+ *
+ * @author Sergej Pavlenko
+ */
+ private FuelStation getSelectedStation() {
+ int idx = comboFuelStations.getSelectedIndex();
+ if (idx < 0 || idx >= Main.fuelStations.size()) {
+ return null;
+ }
+ return Main.fuelStations.get(idx);
+ }
+
+ /**
+ * Create Panel for results of arithmetic operations
+ *
+ * @return result panel
+ *
+ * @author Sergej Pavlenko
+ */
+ private JPanel createResultPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+
+ JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 5, 5));
+
+ JButton btnTotalPrice = new JButton("Total selling value of all available fuel types");
+ btnTotalPrice.addActionListener(e -> {
+ float sum = 0f;
+ for (FuelStation station : Main.fuelStations) {
+ sum += station.get_cumulative_retail_price();
+ }
+ appendOutput(String.format(
+ "Total selling value of all available fuel types: %.2f €", sum));
+ });
+
+ JButton btnHighestPrice = new JButton("Fuel station with highest price (chosen fuel type)");
+ btnHighestPrice.addActionListener(e -> {
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ try {
+ FuelStation fs = Main.getHighestPrice(type);
+ appendOutput("Fuel station with highest price for " + type + ": "
+ + getDisplayName(fs));
+ } catch (NoSuchFuelTypeError ex) {
+ showError("No fitting fuel station with chosen fuel type " + type + " found.");
+ }
+ });
+
+ JButton btnHighestTotalValue = new JButton("Fuel station with highest total value of all available fuel types");
+ btnHighestTotalValue.addActionListener(e -> {
+ try {
+ FuelStation fs = Main.getHighestAccumulatedValue();
+ appendOutput("Fuel station with highest total value of all available fuel types: "
+ + getDisplayName(fs));
+ } catch (NoSuchFuelTypeError ex) {
+ showError("Error when calculating highest total value.");
+ }
+ });
+
+ JButton btnStock = new JButton("Highest / lowest stock (chosen fuel type)");
+ btnStock.addActionListener(e -> {
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ try {
+ FuelStation max = Main.getHighestStoredAmount(type);
+ FuelStation min = Main.getLowestStoredAmount(type);
+ appendOutput("For fuel type " + type + ":\n" +
+ " Highest stock: " + getDisplayName(max) + "\n" +
+ " Lowest stock: " + getDisplayName(min));
+ } catch (NoSuchFuelTypeError ex) {
+ showError("No fitting fuel station with fuel type " + type + " found.");
+ }
+ });
+
+ JButton btnTotalStock = new JButton("Total stock (chosen fuel station, chosen fuel type)");
+ btnTotalStock.addActionListener(e -> {
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ int[] indices = listFuelStations.getSelectedIndices();
+
+ if (indices.length == 0) {
+ showError("Please select at least one fuel station.");
+ return;
+ }
+
+ FuelStation[] selectedStations = new FuelStation[indices.length];
+ for (int i = 0; i < indices.length; i++) {
+ selectedStations[i] = Main.fuelStations.get(indices[i]);
+ }
+
+ float total = Main.getTotalStockLevelOfFuel(type, selectedStations);
+ appendOutput(String.format("Total stock of %s across %d chosen fuel stations: %.2f L", type, indices.length, total));
+ });
+
+ buttonPanel.add(btnTotalPrice);
+ buttonPanel.add(btnHighestPrice);
+ buttonPanel.add(btnHighestTotalValue);
+ buttonPanel.add(btnStock);
+ buttonPanel.add(btnTotalStock);
+
+ JScrollPane listScroller = new JScrollPane(listFuelStations);
+ listScroller.setPreferredSize(new Dimension(250, 0));
+
+ JPanel listPanel = new JPanel(new BorderLayout());
+ listPanel.add(new JLabel("Fuel station selection (multiple possible):"), BorderLayout.NORTH);
+ listPanel.add(listFuelStations, BorderLayout.CENTER);
+
+ panel.add(buttonPanel, BorderLayout.CENTER);
+ panel.add(listScroller, BorderLayout.EAST);
+ panel.add(listPanel, BorderLayout.EAST);
+
+ return panel;
+ }
+
+ /**
+ * Create Panel for price changing
+ *
+ * @return price changing Panel
+ *
+ * @author Sergej Pavlenko
+ */
+ private JPanel createPricePanel() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(5,5,5,5);
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+
+ JLabel lblPrice = new JLabel("New price per litre (€):");
+ JTextField txtPrice = new JTextField(10);
+ JButton btnSave = new JButton("Change price");
+
+ btnSave.addActionListener(e -> {
+ FuelStation station = getSelectedStation();
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ if (station == null) {
+ showError("No fuel station chosen.");
+ return;
+ }
+ try {
+ float newPrice = Float.parseFloat(txtPrice.getText().replace(",", "."));
+ station.set_price(type, newPrice);
+ appendOutput(String.format(
+ "Price changed of %s at %s to %.3f €/L.",
+ type, getDisplayName(station), newPrice
+ ));
+ txtPrice.setText("");
+ } catch (NumberFormatException ex) {
+ showError("Please enter a valid number for the price.");
+ } catch (NoSuchFuelTypeError ex) {
+ showError("This fuel station does not offer the fuel type " + type);
+ }
+ });
+
+ gbc.gridx = 0; gbc.gridy = 0;
+ panel.add(lblPrice, gbc);
+ gbc.gridx = 1;
+ panel.add(txtPrice, gbc);
+
+ gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2;
+ panel.add(btnSave, gbc);
+
+ return panel;
+ }
+
+ /**
+ * Create Panel to change stock
+ *
+ * @author Sergej Pavlenko
+ */
+ private JPanel createStockPanel() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(5,5,5,5);
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+
+ JLabel lblAmount = new JLabel("Amount (litre):");
+ JTextField txtAmount = new JTextField(10);
+ JRadioButton rbAbsolute = new JRadioButton("Set absolute amount", true);
+ JRadioButton rbDelta = new JRadioButton("Change amount (+/-)");
+ ButtonGroup group = new ButtonGroup();
+ group.add(rbAbsolute);
+ group.add(rbDelta);
+
+ JButton btnSave = new JButton("Change stock");
+
+ btnSave.addActionListener(e -> {
+ FuelStation station = getSelectedStation();
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ if (station == null) {
+ showError("No fuel station chosen.");
+ return;
+ }
+ try {
+ float value = Float.parseFloat(txtAmount.getText().replace(",", "."));
+ int capacity = getCapacity(station, type);
+ float current = station.getStored_amount(type);
+
+ if (rbAbsolute.isSelected()) {
+ if (value < 0) {
+ showError("Please enter a positive number");
+ return;
+ }
+ if (value > capacity) {
+ showError("Maximum capacity of " + capacity + " L is being crossed.");
+ return;
+ }
+ station.set_stored_amount(value, type);
+ appendOutput(String.format(
+ "Changed stock of %s at %s to %.2f litre.",
+ type, getDisplayName(station), value
+ ));
+ txtAmount.setText("");
+ } else {
+ float updated = current + value;
+ if (updated < 0) {
+ showError("The amount of fuel may not be negative.");
+ return;
+ }
+ if (updated > capacity) {
+ showError("Maximum capacity of " + capacity + " L is being crossed.");
+ return;
+ }
+
+ station.add_stored_amount(value, type);
+ appendOutput(String.format(
+ "Stock changed of %s at %s by %.2f litre.",
+ type, getDisplayName(station), value
+ ));
+ txtAmount.setText("");
+ }
+ } catch (NumberFormatException ex) {
+ showError("Please enter a valid number for the amount.");
+ } catch (ArithmeticException ex) {
+ showError("Error: " + ex.getMessage());
+ } catch (NoSuchFuelTypeError ex) {
+ showError("This fuel station does not offer the fuel type " + type);
+ }
+ });
+
+ int y = 0;
+ gbc.gridx = 0; gbc.gridy = y;
+ panel.add(lblAmount, gbc);
+ gbc.gridx = 1;
+ panel.add(txtAmount, gbc);
+
+ y++;
+ gbc.gridx = 0; gbc.gridy = y; gbc.gridwidth = 2;
+ panel.add(rbAbsolute, gbc);
+
+ y++;
+ gbc.gridy = y;
+ panel.add(rbDelta, gbc);
+
+ y++;
+ gbc.gridy = y;
+ panel.add(btnSave, gbc);
+
+ return panel;
+ }
+
+ private int getCapacity(FuelStation station, FuelType type) throws NoSuchFuelTypeError {
+ for (Fuel f : station.fuels) {
+ if (f.FUEL_TYPE == type) {
+ return f.CAPACITY;
+ }
+ }
+ throw new NoSuchFuelTypeError("This fuel station does not offer the fuel type " + type);
+ }
+
+ /**
+ * Create Panel for searching for a fuel station with a specific fuel type
+ *
+ * @author Sergej Pavlenko
+ */
+ private JPanel createSearchPanel() {
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+ JButton btnListFuelStations = new JButton("Show fuel stations with chosen fuel type");
+ btnListFuelStations.addActionListener(e -> {
+ FuelType type = (FuelType) comboFuelTypes.getSelectedItem();
+ StringBuilder sb = new StringBuilder();
+ sb.append("Fuel station with fuel type ").append(type).append(":\n");
+ for (FuelStation station : Main.fuelStations) {
+ try {
+ station.getStored_amount(type);
+ sb.append(" - ").append(getDisplayName(station)).append("\n");
+ } catch (NoSuchFuelTypeError ex) {
+ }
+ }
+ appendOutput(sb.toString());
+ });
+
+ panel.add(btnListFuelStations);
+ return panel;
+ }
+
+ /**
+ * Create Panel for making new fuel stations
+ *
+ * @author Sergej Pavlenko
+ */
+ private JPanel createNewFuelStationPanel() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(5, 5, 5, 5);
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.anchor = GridBagConstraints.WEST;
+
+ JRadioButton rbSmall = new JRadioButton("Small fuel station", true);
+ JRadioButton rbMedium = new JRadioButton("Medium fuel station", true);
+ JRadioButton rbLarge = new JRadioButton("Large fuel station", true);
+
+ ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add(rbSmall);
+ buttonGroup.add(rbMedium);
+ buttonGroup.add(rbLarge);
+
+ JLabel lblAtrributes = new JLabel("Special attribute:");
+ JTextField txtAtrributes = new JTextField(15);
+ JLabel lblHint = new JLabel("Small: amount of vending machines, Medium: m^2, Large: supermarket-company");
+
+ JButton btnAdd = new JButton("Add fuel station");
+ btnAdd.addActionListener(e -> {
+ try {
+ if (rbSmall.isSelected()) {
+ short vendingMachines = Short.parseShort(txtAtrributes.getText().trim());
+ Main.addNewFuelStation(vendingMachines);
+ appendOutput("Fuel station with " + vendingMachines + " vending machines has been added.");
+ } else if (rbMedium.isSelected()) {
+ float retailSpace = Float.parseFloat(txtAtrributes.getText().replace(",", ".").trim());
+ Main.addNewFuelStation(retailSpace);
+ appendOutput("Fuel station with " + retailSpace + " m^2 retail space has been added.");
+ } else if (rbLarge.isSelected()) {
+ String supermarketCompany = txtAtrributes.getText().trim();
+ Main.addNewFuelStation(supermarketCompany);
+ appendOutput("Fuel station with supermarket-company" + supermarketCompany + " has been added.");
+ }
+ refreshStationNames();
+ txtAtrributes.setText("");
+ } catch (NumberFormatException ex) {
+ showError("Please enter a valid number (for small / medium).");
+ } catch (IllegalArgumentException ex) {
+ showError(ex.getMessage());
+ }
+ });
+
+ int y = 0;
+ gbc.gridx = 0; gbc.gridy = y;
+ gbc.gridwidth = 3;
+ panel.add(new JLabel("Type of new fuel station:"), gbc);
+
+ y++;
+ gbc.gridy = y;
+ gbc.gridwidth = 1;
+ panel.add(rbSmall, gbc);
+ gbc.gridx = 1;
+ panel.add(rbMedium, gbc);
+ gbc.gridx = 2;
+ panel.add(rbLarge, gbc);
+
+ y++;
+ gbc.gridx = 0; gbc.gridy = y;
+ panel.add(lblAtrributes, gbc);
+ gbc.gridx = 1; gbc.gridwidth = 2;
+ panel.add(txtAtrributes, gbc);
+
+ y++;
+ gbc.gridx = 0; gbc.gridy = y;
+ gbc.gridwidth = 3;
+ panel.add(lblHint, gbc);
+
+ y++;
+ gbc.gridy = y;
+ panel.add(btnAdd, gbc);
+
+ return panel;
+ }
+
++ /**
++ * Create Panel for overview of fuel stations
++ *
++ * @return overview Panel
++ *
++ * @author Sergej Pavlenko
++ */
++ private JPanel createOverviewPanel() {
++ JPanel panel = new JPanel(new BorderLayout());
++
++ fuelTableModel = new DefaultTableModel(new Object[]{"Fuel station", "Station type", "Fuel", "Amount (L)", "Capacity (L)", "Price (€/L)"}, 0) {
++ @Override
++ public boolean isCellEditable(int row, int column) {
++ return false;
++ }
++ };
++
++ tableFuelTypesPerStation = new JTable(fuelTableModel);
++
++ JScrollPane scrollPane = new JScrollPane(tableFuelTypesPerStation);
++
++ JButton btnRefresh = new JButton("Refresh");
++ btnRefresh.addActionListener(e -> refreshFuelTable());
++
++ JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
++ topPanel.add(new JLabel("Overview of all fuels per fuel station"));
++ topPanel.add(btnRefresh);
++
++ panel.add(topPanel, BorderLayout.NORTH);
++ panel.add(scrollPane, BorderLayout.CENTER);
++
++ refreshFuelTable();
++
++ return panel;
++ }
++
+ /**
+ * Updates the selection of fuel stations for ComboBox and List
+ *
+ * @author Sergej Pavlenko
+ */
+ private void refreshStationNames() {
+ String[] names = buildStationNames();
+
+ comboFuelStations.setModel(new DefaultComboBoxModel<>(names));
+
+ listFuelStations.setListData(names);
+ }
+
++ /**
++ * Updates table to keep up with changes
++ *
++ * @author Sergej Pavlenko
++ */
++ private void refreshFuelTable() {
++ if (fuelTableModel == null) {
++ return;
++ }
++
++ fuelTableModel.setRowCount(0);
++
++ for (FuelStation station : Main.fuelStations) {
++ String stationName = getDisplayName(station);
++ String stationType = station.getClass().getSimpleName();
++
++ for (Fuel fuel : station.fuels) {
++ Object[] row = new Object[] {
++ stationName,
++ stationType,
++ fuel.FUEL_TYPE,
++ fuel.getStored_amount(),
++ fuel.CAPACITY,
++ fuel.getPrice()
++ };
++ fuelTableModel.addRow(row);
++ }
++ }
++ }
++
+ private String getDisplayName(FuelStation station) {
+ int index = Main.fuelStations.indexOf(station);
+ if (index >= 0 && index < comboFuelStations.getItemCount()) {
+ return comboFuelStations.getItemAt(index);
+ }
+ return station.getClass().getSimpleName();
+ }
+
++ /**
++ * Creates a text output area
++ *
++ * @param text text output area
++ *
++ * @author Sergej Pavlenko
++ */
+ private void appendOutput(String text) {
+ outputArea.append(text + "\n\n");
+ outputArea.setCaretPosition(outputArea.getDocument().getLength());
+ }
+
++ /**
++ * Creates an error message
++ *
++ * @param message error message
++ *
++ * @author Sergej Pavlenko
++ */
+ private void showError(String message) {
+ JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
+ }
+}