Goal: make the buttons work, show exceptions as dialogs, and split the code into Model, View, Controller.

1. The controller
It converts the raw text from the window into objects and calls the model. It validates input (duplicate ids, "abc" as a duration) but creates no widgets.
src/lms/service/LibraryController.java
package lms.service; import lms.exception.ItemNotAvailableException; import lms.model.*; import java.util.List; /** * LibraryController - the C in MVC (Week 9). * * Model = lms.model + Library (data and rules; knows nothing about windows) * View = lms.ui.MainFrame (windows; knows nothing about the rules) * Controller = this class (receives what the user typed, calls the model) * * The view never touches Book/DVD/Library directly. It asks the controller. * That is what lets us swap the console for Swing - and later swap the * in-memory list for a database - without rewriting the other layers. */ public class LibraryController { private final Library library; public LibraryController(Library library) { this.library = library; } /** Builds the right kind of item from raw text. Throws IllegalArgumentException on bad input. */ public void addItem(String type, String id, String title, String detail) { if (library.hasItem(id)) { throw new IllegalArgumentException("An item with id '" + id + "' already exists"); } switch (type) { case "Book" -> library.addItem(new Book(id, title, detail)); case "DVD" -> library.addItem(new DVD(id, title, parseWholeNumber(detail, "Duration"))); case "Magazine" -> library.addItem(new Magazine(id, title, parseWholeNumber(detail, "Issue number"))); default -> throw new IllegalArgumentException("Unknown item type: " + type); } } public void borrow(String itemId, String memberId) throws ItemNotAvailableException { library.borrowItem(itemId, memberId); } public void giveBack(String itemId) throws ItemNotAvailableException { library.returnItem(itemId); } public List<LibraryItem> getItems() { return library.getItems(); } public List<Member> getMembers() { return library.getMembers(); } private int parseWholeNumber(String text, String label) { try { return Integer.parseInt(text.trim()); } catch (NumberFormatException e) { throw new IllegalArgumentException(label + " must be a whole number"); } } }
2. The view uses only the controller
Look for the three parts of every event: the source (a button), the listener (a lambda), the registration (addActionListener). Look at onBorrow: both Week 5 exceptions become dialogs.
src/lms/ui/MainFrame.java
package lms.ui; import lms.exception.ItemNotAvailableException; import lms.exception.MemberNotFoundException; import lms.model.*; import lms.service.LibraryController; import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; /** * MainFrame - Week 9 (GUI II): events, dialogs and MVC. * * Same layout as Week 8. New this week: * - ActionListeners on the buttons (event-driven programming) * - JOptionPane messages for errors * - the frame talks to a LibraryController, never to the model directly */ public class MainFrame extends JFrame { private final LibraryController controller; private final DefaultTableModel tableModel; private final JTable table; private final JTextField idField = new JTextField(12); private final JTextField titleField = new JTextField(12); private final JTextField detailField = new JTextField(12); private final JComboBox<String> typeBox = new JComboBox<>(new String[]{"Book", "DVD", "Magazine"}); private final JComboBox<String> memberBox = new JComboBox<>(); private final JButton addButton = new JButton("Add Item"); private final JButton borrowButton = new JButton("Borrow"); private final JButton returnButton = new JButton("Return"); private final JLabel statusLabel = new JLabel(" Ready"); public MainFrame(LibraryController controller) { super("Library Management System"); this.controller = controller; // ---- table (CENTER) ---- String[] columns = {"ID", "Title", "Type", "Detail", "Status"}; tableModel = new DefaultTableModel(columns, 0) { @Override public boolean isCellEditable(int row, int col) { return false; } }; table = new JTable(tableModel); table.setRowHeight(24); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getColumnModel().getColumn(1).setPreferredWidth(200); table.getColumnModel().getColumn(3).setPreferredWidth(140); table.getColumnModel().getColumn(4).setPreferredWidth(190); // ---- form (WEST) ---- JPanel form = new JPanel(new GridLayout(0, 2, 6, 8)); form.setBorder(BorderFactory.createTitledBorder("New item")); form.add(new JLabel("Type:")); form.add(typeBox); form.add(new JLabel("ID:")); form.add(idField); form.add(new JLabel("Title:")); form.add(titleField); form.add(new JLabel("Detail:")); form.add(detailField); form.add(new JLabel("")); form.add(addButton); JPanel west = new JPanel(new BorderLayout()); west.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 4)); west.add(form, BorderLayout.NORTH); // ---- title (NORTH) ---- JLabel heading = new JLabel("Library Management System", SwingConstants.CENTER); heading.setFont(heading.getFont().deriveFont(Font.BOLD, 20f)); heading.setBorder(BorderFactory.createEmptyBorder(10, 0, 6, 0)); // ---- buttons + member picker + status (SOUTH) ---- JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 6)); buttons.add(new JLabel("Member:")); buttons.add(memberBox); buttons.add(borrowButton); buttons.add(returnButton); JPanel south = new JPanel(new BorderLayout()); south.add(statusLabel, BorderLayout.WEST); south.add(buttons, BorderLayout.EAST); setLayout(new BorderLayout()); add(heading, BorderLayout.NORTH); add(west, BorderLayout.WEST); add(new JScrollPane(table), BorderLayout.CENTER); add(south, BorderLayout.SOUTH); wireEvents(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(920, 460); setLocationRelativeTo(null); loadMembers(); refreshTable(); } // ------------------------------------------------------------------ // EVENTS: "when this happens, run that" // ------------------------------------------------------------------ private void wireEvents() { addButton.addActionListener(e -> onAdd()); borrowButton.addActionListener(e -> onBorrow()); returnButton.addActionListener(e -> onReturn()); } private void onAdd() { try { controller.addItem((String) typeBox.getSelectedItem(), idField.getText().trim(), titleField.getText(), detailField.getText()); setStatus("Added " + titleField.getText().trim()); idField.setText(""); titleField.setText(""); detailField.setText(""); refreshTable(); } catch (IllegalArgumentException ex) { showError("Cannot add item", ex.getMessage()); } } private void onBorrow() { String itemId = selectedItemId(); if (itemId == null) return; String memberId = ((String) memberBox.getSelectedItem()).split(" - ")[0]; try { controller.borrow(itemId, memberId); setStatus(itemId + " borrowed by " + memberId); refreshTable(); } catch (ItemNotAvailableException ex) { showError("Cannot borrow", ex.getMessage()); } catch (MemberNotFoundException ex) { showError("Unknown member", ex.getMessage()); } } private void onReturn() { String itemId = selectedItemId(); if (itemId == null) return; try { controller.giveBack(itemId); setStatus(itemId + " returned"); refreshTable(); } catch (ItemNotAvailableException ex) { showError("Cannot return", ex.getMessage()); } } // ------------------------------------------------------------------ // helpers // ------------------------------------------------------------------ private String selectedItemId() { int row = table.getSelectedRow(); if (row < 0) { JOptionPane.showMessageDialog(this, "Select a row in the table first.", "Nothing selected", JOptionPane.INFORMATION_MESSAGE); return null; } return (String) tableModel.getValueAt(row, 0); } private void showError(String title, String message) { JOptionPane.showMessageDialog(this, message, title, JOptionPane.WARNING_MESSAGE); setStatus(title + ": " + message); } private void setStatus(String text) { statusLabel.setText(" " + text); } private void loadMembers() { memberBox.removeAllItems(); for (Member m : controller.getMembers()) { memberBox.addItem(m.getMemberId() + " - " + m.getName()); } } private void refreshTable() { tableModel.setRowCount(0); for (LibraryItem item : controller.getItems()) { tableModel.addRow(new Object[]{ item.getId(), item.getTitle(), item.getClass().getSimpleName(), detailOf(item), item.isBorrowed() ? "On loan: " + item.getBorrower().getName() : "Available"}); } } private String detailOf(LibraryItem item) { if (item instanceof Book b) return b.getAuthor(); if (item instanceof DVD d) return d.getDurationMinutes() + " min"; if (item instanceof Magazine m) return "Issue " + m.getIssueNumber(); return ""; } }
3. Wire the layers together
src/lms/GuiMain.java
package lms; import lms.model.*; import lms.service.Library; import lms.service.LibraryController; import lms.ui.MainFrame; import javax.swing.*; /** Wires Model -> Controller -> View and starts the window on the EDT. */ public class GuiMain { public static void main(String[] args) { Library library = new Library(); // MODEL library.addItem(new Book("B001", "Clean Code", "Robert C. Martin")); library.addItem(new Book("B002", "Refactoring", "Martin Fowler")); library.addItem(new DVD("D001", "Introduction to Algorithms", 120)); library.addItem(new Magazine("G001", "Software Weekly", 42)); library.addMember(new Student("M001", "Nadeesha Perera")); library.addMember(new Staff("M002", "Mr. Kasun Silva")); LibraryController controller = new LibraryController(library); // CONTROLLER SwingUtilities.invokeLater(() -> new MainFrame(controller).setVisible(true)); // VIEW } }
4. Remove printing from the model
Delete the System.out.println calls from LibraryItem.loan/returnItem and from the Library.addItem overloads. A model that prints cannot serve a window.
5. Try it
mkdir -p out
javac -d out $(find src -name "*.java")
java -cp out lms.GuiMain



Check MainFrame contains no new Book(...) and no Library calls. If it does, move that logic into the controller.
Checkpoint: is it done?
- Add, Borrow and Return work and refresh the table
- Every failure shows a dialog with a clear message; nothing crashes
MainFrametalks only toLibraryController; the model prints nothing- Three screenshots in
docs/; PR: Week 9: GUI II (Events, Dialogs & MVC)