hw3: add javadoc everywhere

This commit is contained in:
Yuri Tatishchev 2025-03-26 20:45:09 -07:00
parent c10ba4b2fc
commit f098922149
Signed by: CaZzzer
GPG Key ID: E0EBF441EA424369
11 changed files with 346 additions and 10 deletions

View File

@ -1,8 +1,16 @@
import controller.PhotoAlbumController; import controller.PhotoAlbumController;
import model.PhotoAlbumModel; import model.PhotoAlbumModel;
import strategy.SortByDate;
import view.PhotoAlbumView; import view.PhotoAlbumView;
/**
* Photo Album Manager application entry point.
* <p>
* 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 class PhotoAlbumApp {
public static void main(String[] args) { public static void main(String[] args) {
// Create MVC components // Create MVC components
@ -16,4 +24,4 @@ public class PhotoAlbumApp {
// Display the main window // Display the main window
javax.swing.SwingUtilities.invokeLater(() -> view.setVisible(true)); javax.swing.SwingUtilities.invokeLater(() -> view.setVisible(true));
} }
} }

View File

@ -12,22 +12,55 @@ import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.File; import java.io.File;
import java.util.Date; import java.util.Date;
/**
* A controller that manages interactions between the photo album model and view.
* <p>
* 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 { public class PhotoAlbumController {
private final PhotoAlbumModel model; private final PhotoAlbumModel model;
private final PhotoAlbumView view; 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) { public PhotoAlbumController(PhotoAlbumModel model, PhotoAlbumView view) {
this.model = model; this.model = model;
this.view = view; this.view = view;
} }
/**
* Returns the photo album model being controlled.
*
* @return the photo album model
*/
public PhotoAlbumModel getModel() { public PhotoAlbumModel getModel() {
return model; 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() { public void handleAddPhoto() {
JFileChooser fileChooser = new JFileChooser(); 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) { if (fileChooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile(); 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() { public void handleDeletePhoto() {
String name = JOptionPane.showInputDialog(view, "Enter photo name to delete:"); String name = JOptionPane.showInputDialog(view, "Enter photo name to delete:");
if (name != null && !name.trim().isEmpty()) { 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() { public void handleNext() {
if (model.hasNext()) { if (model.hasNext()) {
model.next(); model.next();
} }
} }
/**
* Handles navigation to the previous photo in the album.
* Moves to the previous photo if one exists.
*/
public void handlePrevious() { public void handlePrevious() {
if (model.hasPrevious()) { if (model.hasPrevious()) {
model.previous(); model.previous();
} }
} }
/**
* Handles changing the sorting strategy for photos in the album.
*
* @param strategy the sorting strategy to use:
* <ul>
* <li>0 for name-based sorting,
* <li>1 for date-based sorting,
* <li>2 for size-based sorting
* </ul>
*/
public void handleSort(int strategy) { public void handleSort(int strategy) {
switch (strategy) { switch (strategy) {
case 0: case 0:

View File

@ -4,10 +4,32 @@ import model.Photo;
import java.util.Iterator; import java.util.Iterator;
/**
* Interface for album iterators.
*/
public interface AlbumIterator extends Iterator<Photo> { public interface AlbumIterator extends Iterator<Photo> {
boolean hasNext(); // to check if there is a next element 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 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 current(); // to get the photo at the current position
Photo next(); // to advance the iterator to the next 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 Photo previous(); // to advance the iterator to the previous position
} }

View File

@ -1,13 +1,37 @@
package iterator; package iterator;
import model.Photo; import model.Photo;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
/**
* An iterator implementation for navigating through photos in a photo album.
* <p>
* 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.
* <p>
* 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 { public class PhotoIterator implements AlbumIterator {
private final List<Photo> photos; private final List<Photo> photos;
private int currentPosition; 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<Photo> photos) { public PhotoIterator(List<Photo> photos) {
this.photos = photos; this.photos = photos;
this.currentPosition = 0; this.currentPosition = 0;
@ -31,6 +55,9 @@ public class PhotoIterator implements AlbumIterator {
return photos.get(currentPosition); return photos.get(currentPosition);
} }
/**
* @throws NoSuchElementException if there is no next photo
*/
@Override @Override
public Photo next() { public Photo next() {
if (!hasNext()) { if (!hasNext()) {
@ -39,6 +66,9 @@ public class PhotoIterator implements AlbumIterator {
return photos.get(++currentPosition); return photos.get(++currentPosition);
} }
/**
* @throws NoSuchElementException if there is no previous photo
*/
@Override @Override
public Photo previous() { public Photo previous() {
if (!hasPrevious()) { if (!hasPrevious()) {

View File

@ -2,5 +2,21 @@ package model;
import java.util.Date; import java.util.Date;
/**
* A record class that represents a photo.
* <p>
* A photo has a:
* <ul>
* <li>name
* <li>file path
* <li>date added
* <li>file size
* </ul>
* <p>
* A photo is immutable.
*
* @author Yuri Tatishchev
* @version 0.1 2022-03-26
*/
public record Photo(String name, String filePath, Date dateAdded, long fileSize) { public record Photo(String name, String filePath, Date dateAdded, long fileSize) {
} }

View File

@ -3,15 +3,40 @@ package model;
import strategy.SortingStrategy; import strategy.SortingStrategy;
import iterator.AlbumIterator; import iterator.AlbumIterator;
import iterator.PhotoIterator; import iterator.PhotoIterator;
import java.util.*; import java.util.*;
/**
* A model that represents a photo album.
* <p>
* 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 { public class PhotoAlbumModel {
private List<Photo> photos; private List<Photo> photos;
private SortingStrategy<Photo> sortingStrategy; private SortingStrategy<Photo> sortingStrategy;
private final List<ModelChangeListener> listeners; private final List<ModelChangeListener> listeners;
private AlbumIterator iterator; private AlbumIterator iterator;
/**
* A listener interface for model changes.
* The method {@link #onModelChanged()} is called when the model changes.
*/
public interface ModelChangeListener { public interface ModelChangeListener {
/**
* Called when the model changes.
*/
void onModelChanged(); void onModelChanged();
} }
@ -21,24 +46,40 @@ public class PhotoAlbumModel {
iterator = new PhotoIterator(photos); 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) { public void addPhoto(Photo photo) {
photos.add(photo); photos.add(photo);
sortPhotos(); sortPhotos();
iterator = new PhotoIterator(photos);
notifyListeners(); 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) { public void deletePhoto(String name) {
Photo currentPhoto = iterator.current(); Photo currentPhoto = iterator.current();
photos.removeIf(photo -> photo.name().equals(name)); photos.removeIf(photo -> photo.name().equals(name));
if (photos.isEmpty()) { if (photos.isEmpty() || (currentPhoto != null && currentPhoto.name().equals(name))) {
iterator = new PhotoIterator(photos);
} else if (currentPhoto != null && currentPhoto.name().equals(name)) {
iterator = new PhotoIterator(photos); iterator = new PhotoIterator(photos);
} }
notifyListeners(); 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<Photo> strategy) { public void setSortingStrategy(SortingStrategy<Photo> strategy) {
this.sortingStrategy = strategy; this.sortingStrategy = strategy;
sortPhotos(); 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) { public void addListener(ModelChangeListener listener) {
listeners.add(listener); listeners.add(listener);
} }
/**
* Notifies all listeners that the model has changed,
* calling the {@link ModelChangeListener#onModelChanged()} method.
*/
private void notifyListeners() { private void notifyListeners() {
for (ModelChangeListener listener : listeners) { for (ModelChangeListener listener : listeners) {
listener.onModelChanged(); listener.onModelChanged();
} }
} }
/**
* Returns an unmodifiable list of photos in the album.
*
* @return an unmodifiable list of photos
*/
public List<Photo> getPhotos() { public List<Photo> getPhotos() {
return Collections.unmodifiableList(photos); return Collections.unmodifiableList(photos);
} }
/**
* Returns the photo in the album that the iterator is currently pointing to.
*
* @return the current photo
*/
public Photo getCurrentPhoto() { public Photo getCurrentPhoto() {
try { try {
return iterator.current(); 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() { public boolean hasNext() {
return iterator.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() { public boolean hasPrevious() {
return iterator.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() { public Photo next() {
try { try {
Photo next = iterator.next(); 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() { public Photo previous() {
try { try {
Photo prev = iterator.previous(); Photo prev = iterator.previous();

View File

@ -5,6 +5,9 @@ import model.Photo;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
/**
* A sorting strategy that sorts photos by date.
*/
public class SortByDate implements SortingStrategy<Photo> { public class SortByDate implements SortingStrategy<Photo> {
@Override @Override
public List<Photo> sort(List<Photo> photos) { public List<Photo> sort(List<Photo> photos) {

View File

@ -5,6 +5,9 @@ import model.Photo;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
/**
* A sorting strategy that sorts photos by name.
*/
public class SortByName implements SortingStrategy<Photo> { public class SortByName implements SortingStrategy<Photo> {
@Override @Override
public List<Photo> sort(List<Photo> photos) { public List<Photo> sort(List<Photo> photos) {

View File

@ -5,6 +5,9 @@ import model.Photo;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
/**
* A sorting strategy that sorts photos by size.
*/
public class SortBySize implements SortingStrategy<Photo> { public class SortBySize implements SortingStrategy<Photo> {
@Override @Override
public List<Photo> sort(List<Photo> photos) { public List<Photo> sort(List<Photo> photos) {

View File

@ -2,6 +2,11 @@ package strategy;
import java.util.List; import java.util.List;
/**
* Interface for sorting strategies.
*
* @param <T> the type of elements to be sorted
*/
public interface SortingStrategy<T> { public interface SortingStrategy<T> {
List<T> sort(List<T> photos); List<T> sort(List<T> photos);
} }

View File

@ -10,6 +10,29 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* A view that displays a photo album user interface.
* <p>
* 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.
* <p>
* The view provides user interface components for:
* <ul>
* <li>Displaying a list of photos with thumbnails and details
* <li>Showing the currently selected photo
* <li>Navigation controls (previous/next)
* <li>Photo management (add/delete)
* <li>Sorting options
* </ul>
*
* @author Yuri Tatishchev
* @version 0.1 2025-03-26
* @see PhotoAlbumModel
* @see PhotoAlbumController
* @see Photo
*/
public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChangeListener { public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChangeListener {
private PhotoAlbumModel model; private PhotoAlbumModel model;
@ -27,6 +50,12 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
setupLayout(); setupLayout();
} }
/**
* Initializes all UI components of the photo album view.
* <p>
* Sets up the frame properties, creates buttons, list components,
* and initializes them with default states.
*/
private void initializeComponents() { private void initializeComponents() {
setTitle("Photo Album Manager"); setTitle("Photo Album Manager");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 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"}; String[] sortOptions = {"Sort by Name", "Sort by Date", "Sort by Size"};
sortingCombo = new JComboBox<>(sortOptions); sortingCombo = new JComboBox<>(sortOptions);
// Select default sorting option
sortingCombo.setSelectedIndex(1);
listModel = new DefaultListModel<>(); listModel = new DefaultListModel<>();
photoList = new JList<>(listModel); photoList = new JList<>(listModel);
@ -57,6 +84,17 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
photoList.setFixedCellHeight(60); // Accommodate thumbnails photoList.setFixedCellHeight(60); // Accommodate thumbnails
} }
/**
* Sets up the layout of the photo album view.
* <p>
* Arranges the UI components using BorderLayout with:
* <ul>
* <li>Photo list in the WEST
* <li>Current photo display in the CENTER
* <li>Control buttons in the SOUTH
* </ul>
*/
private void setupLayout() { private void setupLayout() {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
@ -80,6 +118,14 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
add(controlPanel, BorderLayout.SOUTH); add(controlPanel, BorderLayout.SOUTH);
} }
/**
* Sets the controller for the photo album view.
* <p>
* 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) { public void setController(PhotoAlbumController controller) {
this.model = controller.getModel(); this.model = controller.getModel();
model.addListener(this); model.addListener(this);
@ -89,6 +135,9 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
nextButton.addActionListener(e -> controller.handleNext()); nextButton.addActionListener(e -> controller.handleNext());
previousButton.addActionListener(e -> controller.handlePrevious()); previousButton.addActionListener(e -> controller.handlePrevious());
sortingCombo.addActionListener(e -> controller.handleSort(sortingCombo.getSelectedIndex())); sortingCombo.addActionListener(e -> controller.handleSort(sortingCombo.getSelectedIndex()));
// Select default sorting option
sortingCombo.setSelectedIndex(1);
} }
@Override @Override
@ -98,6 +147,12 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
updateNavigationButtons(); updateNavigationButtons();
} }
/**
* Updates the list of photos displayed in the sidebar.
* <p>
* Clears the current list model and populates it with
* names of photos from the model.
*/
private void updatePhotoList() { private void updatePhotoList() {
listModel.clear(); listModel.clear();
List<Photo> photos = model.getPhotos(); List<Photo> 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.
* <p>
* 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() { private void updateCurrentPhoto() {
Photo current = model.getCurrentPhoto(); Photo current = model.getCurrentPhoto();
if (current != null) { if (current != null) {
@ -123,12 +185,27 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
} }
} }
/**
* Updates the enabled state of navigation buttons.
* <p>
* 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() { private void updateNavigationButtons() {
previousButton.setEnabled(model.hasPrevious()); previousButton.setEnabled(model.hasPrevious());
nextButton.setEnabled(model.hasNext()); nextButton.setEnabled(model.hasNext());
deleteButton.setEnabled(model.getCurrentPhoto() != null); deleteButton.setEnabled(model.getCurrentPhoto() != null);
} }
/**
* Loads and scales an image from the given path.
* <p>
* 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) { private ImageIcon loadImage(String path) {
try { try {
ImageIcon icon = new ImageIcon(path); 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.
* <p>
* This inner class renders each photo in the list with:
* <ul>
* <li>A thumbnail image
* <li>The photo name
* <li>Additional details (date and file size)
* </ul>
* <p>
* It also implements thumbnail caching to improve performance.
*/
private class PhotoListCellRenderer extends JPanel implements ListCellRenderer<String> { private class PhotoListCellRenderer extends JPanel implements ListCellRenderer<String> {
private final JLabel imageLabel = new JLabel(); private final JLabel imageLabel = new JLabel();
private final JLabel textLabel = new JLabel(); private final JLabel textLabel = new JLabel();
@ -170,7 +259,11 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
@Override @Override
public Component getListCellRendererComponent(JList<? extends String> list, public Component getListCellRendererComponent(JList<? extends String> list,
String name, int index, boolean isSelected, boolean cellHasFocus) { String name,
int index,
boolean isSelected,
boolean cellHasFocus
) {
textLabel.setText(name); textLabel.setText(name);
Photo photo = model.getPhotos().get(index); Photo photo = model.getPhotos().get(index);
@ -187,6 +280,14 @@ public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChang
return this; return this;
} }
/**
* Formats a file size in bytes to a human-readable string.
* <p>
* 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) { private String formatFileSize(long size) {
if (size < 1024 * 1024) { if (size < 1024 * 1024) {
return String.format("%.1f KB", size / 1024.0); 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.
* <p>
* 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) { private ImageIcon loadThumbnail(String path) {
try { try {
ImageIcon icon = new ImageIcon(path); ImageIcon icon = new ImageIcon(path);