diff --git a/hw3/src/PhotoAlbumApp.java b/hw3/src/PhotoAlbumApp.java index 6da1d8c..0eb660f 100644 --- a/hw3/src/PhotoAlbumApp.java +++ b/hw3/src/PhotoAlbumApp.java @@ -1,8 +1,16 @@ import controller.PhotoAlbumController; import model.PhotoAlbumModel; -import strategy.SortByDate; import view.PhotoAlbumView; + +/** + * Photo Album Manager application entry point. + *

+ * This class creates the model, view, and controller objects and initializes the view. + * + * @author Yuri Tatishchev + * @version 0.1 2025-03-26 + */ public class PhotoAlbumApp { public static void main(String[] args) { // Create MVC components @@ -16,4 +24,4 @@ public class PhotoAlbumApp { // Display the main window javax.swing.SwingUtilities.invokeLater(() -> view.setVisible(true)); } -} \ No newline at end of file +} diff --git a/hw3/src/controller/PhotoAlbumController.java b/hw3/src/controller/PhotoAlbumController.java index a67e16f..0a02a89 100644 --- a/hw3/src/controller/PhotoAlbumController.java +++ b/hw3/src/controller/PhotoAlbumController.java @@ -12,22 +12,55 @@ import javax.swing.filechooser.FileNameExtensionFilter; import java.io.File; import java.util.Date; +/** + * A controller that manages interactions between the photo album model and view. + *

+ * This class handles user actions from the view, such as adding and deleting photos, + * navigating through the album, and changing the sorting strategy. + * It acts as an intermediary between the {@link PhotoAlbumModel} and {@link PhotoAlbumView}, + * translating user interface events into model operations. + * + * @author Yuri Tatishchev + * @version 0.1 2025-03-26 + * @see PhotoAlbumModel + * @see PhotoAlbumView + * @see Photo + */ public class PhotoAlbumController { private final PhotoAlbumModel model; private final PhotoAlbumView view; + /** + * Constructs a new photo album controller. + * + * @param model the photo album model to control + * @param view the view to handle user interactions + */ public PhotoAlbumController(PhotoAlbumModel model, PhotoAlbumView view) { this.model = model; this.view = view; } + /** + * Returns the photo album model being controlled. + * + * @return the photo album model + */ public PhotoAlbumModel getModel() { return model; } + /** + * Handles the addition of a new photo to the album. + * Opens a file chooser dialog for selecting an image file and + * prompts for a photo name. + */ public void handleAddPhoto() { JFileChooser fileChooser = new JFileChooser(); - fileChooser.setFileFilter(new FileNameExtensionFilter("Image files", "jpg", "jpeg", "png", "gif")); + fileChooser.setFileFilter(new FileNameExtensionFilter( + "Image files", + "jpg", "jpeg", "png", "gif" + )); if (fileChooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); @@ -45,6 +78,10 @@ public class PhotoAlbumController { } } + /** + * Handles the deletion of a photo from the album. + * Prompts for the name of the photo to delete. + */ public void handleDeletePhoto() { String name = JOptionPane.showInputDialog(view, "Enter photo name to delete:"); if (name != null && !name.trim().isEmpty()) { @@ -52,18 +89,36 @@ public class PhotoAlbumController { } } + /** + * Handles navigation to the next photo in the album. + * Moves to the next photo if one exists. + */ public void handleNext() { if (model.hasNext()) { model.next(); } } + /** + * Handles navigation to the previous photo in the album. + * Moves to the previous photo if one exists. + */ public void handlePrevious() { if (model.hasPrevious()) { model.previous(); } } + /** + * Handles changing the sorting strategy for photos in the album. + * + * @param strategy the sorting strategy to use: + *

+ */ public void handleSort(int strategy) { switch (strategy) { case 0: diff --git a/hw3/src/iterator/AlbumIterator.java b/hw3/src/iterator/AlbumIterator.java index 5c29213..95a63a3 100644 --- a/hw3/src/iterator/AlbumIterator.java +++ b/hw3/src/iterator/AlbumIterator.java @@ -4,10 +4,32 @@ import model.Photo; import java.util.Iterator; +/** + * Interface for album iterators. + */ public interface AlbumIterator extends Iterator { boolean hasNext(); // to check if there is a next element + + /** + * Returns whether there is a previous photo in the album. + * + * @return {@code true} if there is a previous photo, {@code false} otherwise + */ boolean hasPrevious(); // to check if there is a previous element + + /** + * Returns the current photo in the album. + * + * @return the current photo + */ Photo current(); // to get the photo at the current position + Photo next(); // to advance the iterator to the next position + + /** + * Moves to and returns the previous photo in the album. + * + * @return the previous photo + */ Photo previous(); // to advance the iterator to the previous position } diff --git a/hw3/src/iterator/PhotoIterator.java b/hw3/src/iterator/PhotoIterator.java index a9e7b8f..fc6a12d 100644 --- a/hw3/src/iterator/PhotoIterator.java +++ b/hw3/src/iterator/PhotoIterator.java @@ -1,13 +1,37 @@ package iterator; import model.Photo; + import java.util.List; import java.util.NoSuchElementException; +/** + * An iterator implementation for navigating through photos in a photo album. + *

+ * This class provides functionality to traverse a list of photos in both + * forward and backward directions. It maintains a current position and + * provides methods to check for the existence of next and previous photos, + * as well as to retrieve the current, next, and previous photos. + *

+ * The iterator throws {@link NoSuchElementException} when attempting to + * access elements beyond the bounds of the photo list. + * + * @author Yuri Tatishchev + * @version 0.1 2025-03-26 + * @see Photo + * @see AlbumIterator + * @see NoSuchElementException + */ public class PhotoIterator implements AlbumIterator { private final List photos; private int currentPosition; + /** + * Constructs a new photo iterator for the given list of photos. + * The iterator starts at position 0. + * + * @param photos the list of photos to iterate over + */ public PhotoIterator(List photos) { this.photos = photos; this.currentPosition = 0; @@ -31,6 +55,9 @@ public class PhotoIterator implements AlbumIterator { return photos.get(currentPosition); } + /** + * @throws NoSuchElementException if there is no next photo + */ @Override public Photo next() { if (!hasNext()) { @@ -39,6 +66,9 @@ public class PhotoIterator implements AlbumIterator { return photos.get(++currentPosition); } + /** + * @throws NoSuchElementException if there is no previous photo + */ @Override public Photo previous() { if (!hasPrevious()) { diff --git a/hw3/src/model/Photo.java b/hw3/src/model/Photo.java index d1d7c3b..bbd203e 100644 --- a/hw3/src/model/Photo.java +++ b/hw3/src/model/Photo.java @@ -2,5 +2,21 @@ package model; import java.util.Date; +/** + * A record class that represents a photo. + *

+ * A photo has a: + *

+ *

+ * A photo is immutable. + * + * @author Yuri Tatishchev + * @version 0.1 2022-03-26 + */ public record Photo(String name, String filePath, Date dateAdded, long fileSize) { } diff --git a/hw3/src/model/PhotoAlbumModel.java b/hw3/src/model/PhotoAlbumModel.java index 67c211c..015b6d4 100644 --- a/hw3/src/model/PhotoAlbumModel.java +++ b/hw3/src/model/PhotoAlbumModel.java @@ -3,15 +3,40 @@ package model; import strategy.SortingStrategy; import iterator.AlbumIterator; import iterator.PhotoIterator; + import java.util.*; +/** + * A model that represents a photo album. + *

+ * This class is responsible for managing the photos in the album, as well as + * the sorting strategy used to sort the photos. + * The model notifies its listeners when the model changes. + * It also provides methods to add and delete photos, + * as well as to navigate through the photos. + * + * @author Yuri Tatishchev + * @version 0.1 2025-03-26 + * @see Photo + * @see SortingStrategy + * @see AlbumIterator + * @see PhotoIterator + * @see ModelChangeListener + */ public class PhotoAlbumModel { private List photos; private SortingStrategy sortingStrategy; private final List listeners; private AlbumIterator iterator; + /** + * A listener interface for model changes. + * The method {@link #onModelChanged()} is called when the model changes. + */ public interface ModelChangeListener { + /** + * Called when the model changes. + */ void onModelChanged(); } @@ -21,24 +46,40 @@ public class PhotoAlbumModel { iterator = new PhotoIterator(photos); } + /** + * Adds a photo to the album. + * Resets the iterator and notifies the listeners. + * + * @param photo the photo to add + */ public void addPhoto(Photo photo) { photos.add(photo); sortPhotos(); - iterator = new PhotoIterator(photos); notifyListeners(); } + /** + * Deletes a photo from the album by name. + * If the deleted photo is the current photo or the album is empty, + * the iterator is reset. + * + * @param name the name of the photo to delete + */ public void deletePhoto(String name) { Photo currentPhoto = iterator.current(); photos.removeIf(photo -> photo.name().equals(name)); - if (photos.isEmpty()) { - iterator = new PhotoIterator(photos); - } else if (currentPhoto != null && currentPhoto.name().equals(name)) { + if (photos.isEmpty() || (currentPhoto != null && currentPhoto.name().equals(name))) { iterator = new PhotoIterator(photos); } notifyListeners(); } + /** + * Sets the sorting strategy for the photos. + * Sorts the photos using the new strategy and resets the iterator. + * + * @param strategy the sorting strategy to set + */ public void setSortingStrategy(SortingStrategy strategy) { this.sortingStrategy = strategy; sortPhotos(); @@ -53,20 +94,39 @@ public class PhotoAlbumModel { } } + /** + * Adds a listener for model changes. + * + * @param listener the listener to add + */ public void addListener(ModelChangeListener listener) { listeners.add(listener); } + /** + * Notifies all listeners that the model has changed, + * calling the {@link ModelChangeListener#onModelChanged()} method. + */ private void notifyListeners() { for (ModelChangeListener listener : listeners) { listener.onModelChanged(); } } + /** + * Returns an unmodifiable list of photos in the album. + * + * @return an unmodifiable list of photos + */ public List getPhotos() { return Collections.unmodifiableList(photos); } + /** + * Returns the photo in the album that the iterator is currently pointing to. + * + * @return the current photo + */ public Photo getCurrentPhoto() { try { return iterator.current(); @@ -75,14 +135,30 @@ public class PhotoAlbumModel { } } + /** + * Returns whether there is a next photo in the album. + * + * @return {@code true} if there is a next photo, {@code false} otherwise + */ public boolean hasNext() { return iterator.hasNext(); } + /** + * Returns whether there is a previous photo in the album. + * + * @return {@code true} if there is a previous photo, {@code false} otherwise + */ public boolean hasPrevious() { return iterator.hasPrevious(); } + /** + * Advances the iterator to the next photo in the album. + * Notifies the listeners. + * + * @return the next photo or {@code null} if there is no next photo + */ public Photo next() { try { Photo next = iterator.next(); @@ -93,6 +169,12 @@ public class PhotoAlbumModel { } } + /** + * Advances the iterator to the previous photo in the album. + * Notifies the listeners. + * + * @return the previous photo or {@code null} if there is no previous photo + */ public Photo previous() { try { Photo prev = iterator.previous(); diff --git a/hw3/src/strategy/SortByDate.java b/hw3/src/strategy/SortByDate.java index 0a67528..35d237a 100644 --- a/hw3/src/strategy/SortByDate.java +++ b/hw3/src/strategy/SortByDate.java @@ -5,6 +5,9 @@ import model.Photo; import java.util.Comparator; import java.util.List; +/** + * A sorting strategy that sorts photos by date. + */ public class SortByDate implements SortingStrategy { @Override public List sort(List photos) { diff --git a/hw3/src/strategy/SortByName.java b/hw3/src/strategy/SortByName.java index 15b1ad4..59a5b49 100644 --- a/hw3/src/strategy/SortByName.java +++ b/hw3/src/strategy/SortByName.java @@ -5,6 +5,9 @@ import model.Photo; import java.util.Comparator; import java.util.List; +/** + * A sorting strategy that sorts photos by name. + */ public class SortByName implements SortingStrategy { @Override public List sort(List photos) { diff --git a/hw3/src/strategy/SortBySize.java b/hw3/src/strategy/SortBySize.java index 8d3067f..6088413 100644 --- a/hw3/src/strategy/SortBySize.java +++ b/hw3/src/strategy/SortBySize.java @@ -5,6 +5,9 @@ import model.Photo; import java.util.Comparator; import java.util.List; +/** + * A sorting strategy that sorts photos by size. + */ public class SortBySize implements SortingStrategy { @Override public List sort(List photos) { diff --git a/hw3/src/strategy/SortingStrategy.java b/hw3/src/strategy/SortingStrategy.java index 83b209f..9460791 100644 --- a/hw3/src/strategy/SortingStrategy.java +++ b/hw3/src/strategy/SortingStrategy.java @@ -2,6 +2,11 @@ package strategy; import java.util.List; +/** + * Interface for sorting strategies. + * + * @param the type of elements to be sorted + */ public interface SortingStrategy { List sort(List photos); } diff --git a/hw3/src/view/PhotoAlbumView.java b/hw3/src/view/PhotoAlbumView.java index f031a36..0b2bba0 100644 --- a/hw3/src/view/PhotoAlbumView.java +++ b/hw3/src/view/PhotoAlbumView.java @@ -10,6 +10,29 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * A view that displays a photo album user interface. + *

+ * This class is responsible for displaying the photo album's graphical interface, + * including the list of photos, current photo display, and control buttons. + * The view implements the {@link PhotoAlbumModel.ModelChangeListener} interface + * to receive notifications of model changes. + *

+ * The view provides user interface components for: + *

+ * + * @author Yuri Tatishchev + * @version 0.1 2025-03-26 + * @see PhotoAlbumModel + * @see PhotoAlbumController + * @see Photo + */ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChangeListener { private PhotoAlbumModel model; @@ -27,6 +50,12 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang setupLayout(); } + /** + * Initializes all UI components of the photo album view. + *

+ * Sets up the frame properties, creates buttons, list components, + * and initializes them with default states. + */ private void initializeComponents() { setTitle("Photo Album Manager"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -47,8 +76,6 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang String[] sortOptions = {"Sort by Name", "Sort by Date", "Sort by Size"}; sortingCombo = new JComboBox<>(sortOptions); - // Select default sorting option - sortingCombo.setSelectedIndex(1); listModel = new DefaultListModel<>(); photoList = new JList<>(listModel); @@ -57,6 +84,17 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang photoList.setFixedCellHeight(60); // Accommodate thumbnails } + + /** + * Sets up the layout of the photo album view. + *

+ * Arranges the UI components using BorderLayout with: + *

+ */ private void setupLayout() { setLayout(new BorderLayout()); @@ -80,6 +118,14 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang add(controlPanel, BorderLayout.SOUTH); } + /** + * Sets the controller for the photo album view. + *

+ * Attaches the controller to the view and sets up event listeners + * for the control buttons and sorting combo box. + * + * @param controller the controller to set + */ public void setController(PhotoAlbumController controller) { this.model = controller.getModel(); model.addListener(this); @@ -89,6 +135,9 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang nextButton.addActionListener(e -> controller.handleNext()); previousButton.addActionListener(e -> controller.handlePrevious()); sortingCombo.addActionListener(e -> controller.handleSort(sortingCombo.getSelectedIndex())); + + // Select default sorting option + sortingCombo.setSelectedIndex(1); } @Override @@ -98,6 +147,12 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang updateNavigationButtons(); } + /** + * Updates the list of photos displayed in the sidebar. + *

+ * Clears the current list model and populates it with + * names of photos from the model. + */ private void updatePhotoList() { listModel.clear(); List photos = model.getPhotos(); @@ -106,6 +161,13 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang } } + /** + * Updates the display of the current photo in the main view area. + *

+ * If a photo is selected, loads and displays its image. + * If loading fails, displays an error message. + * If no photo is selected, displays a default message. + */ private void updateCurrentPhoto() { Photo current = model.getCurrentPhoto(); if (current != null) { @@ -123,12 +185,27 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang } } + /** + * Updates the enabled state of navigation buttons. + *

+ * Enables or disables the previous/next buttons based on + * the current position in the photo album. + * Enables the delete button only when the album is not empty. + */ private void updateNavigationButtons() { previousButton.setEnabled(model.hasPrevious()); nextButton.setEnabled(model.hasNext()); deleteButton.setEnabled(model.getCurrentPhoto() != null); } + /** + * Loads and scales an image from the given path. + *

+ * Creates a scaled version of the image suitable for the main display area. + * + * @param path the file path of the image to load + * @return a scaled ImageIcon, or null if loading fails + */ private ImageIcon loadImage(String path) { try { ImageIcon icon = new ImageIcon(path); @@ -140,6 +217,18 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang } } + /** + * A custom list cell renderer for displaying photos with thumbnails and details. + *

+ * This inner class renders each photo in the list with: + *

+ *

+ * It also implements thumbnail caching to improve performance. + */ private class PhotoListCellRenderer extends JPanel implements ListCellRenderer { private final JLabel imageLabel = new JLabel(); private final JLabel textLabel = new JLabel(); @@ -170,7 +259,11 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang @Override public Component getListCellRendererComponent(JList list, - String name, int index, boolean isSelected, boolean cellHasFocus) { + String name, + int index, + boolean isSelected, + boolean cellHasFocus + ) { textLabel.setText(name); Photo photo = model.getPhotos().get(index); @@ -187,6 +280,14 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang return this; } + /** + * Formats a file size in bytes to a human-readable string. + *

+ * Converts the size to kilobytes or megabytes with one decimal place. + * + * @param size the file size in bytes + * @return a formatted string with the size in KB or MB + */ private String formatFileSize(long size) { if (size < 1024 * 1024) { return String.format("%.1f KB", size / 1024.0); @@ -195,6 +296,14 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang } } + /** + * Loads and scales a thumbnail image from the given path. + *

+ * Creates a scaled version of the image suitable for the list display. + * + * @param path the file path of the image to load + * @return a scaled ImageIcon, or null if loading fails + */ private ImageIcon loadThumbnail(String path) { try { ImageIcon icon = new ImageIcon(path);