Skip to content

ItunesPlaylist#9

Closed
johanbriger wants to merge 35 commits intomainfrom
ItunesTest
Closed

ItunesPlaylist#9
johanbriger wants to merge 35 commits intomainfrom
ItunesTest

Conversation

@johanbriger
Copy link

@johanbriger johanbriger commented Jan 5, 2026

Summary by CodeRabbit

  • New Features

    • Launched a music library application featuring an iPod-inspired graphical interface with virtual click wheel navigation.
    • Integrated iTunes search to discover and add music to your library.
    • Create, manage, and organize custom playlists.
    • Browse music by artists, albums, and genres.
    • Display song details on an LCD-style screen.
  • Chores

    • Added Docker and database configuration for music data persistence.

✏️ Tip: You can customize this high-level summary in your review settings.

github-classroom bot and others added 30 commits December 15, 2025 10:16
Adds shutdown hook to close EntityManagerFactory

Co-authored-by: Simon Forsberg <simon.co.forsberg@gmail.com>
Co-authored-by: Jesper Larsson <jesper.larsson@iths.se>
Co-authored-by: Simon Forsberg <simon.co.forsberg@gmail.com>
Co-authored-by: Jesper Larsson <jesper.larsson@iths.se>
…hen loading DB.

Co-authored-by: Simon Forsberg <simon.co.forsberg@gmail.com>

Co-authored-by: Jesper Larsson <jesper.larsson@iths.se>

Co-authored-by: Johan Briger <johanbriger@gmail.com>
Implements ItunesApiConnection and Databaseinitializer for loading DB on startup
Co-Authored-By: Mattias Hagström  <mattiashagstrommusic@gmail.com>
Co-authored-by: Simon Forsberg <simon.co.forsberg@gmail.com>
Co-authored-by: Johan Briger <johanbriger@gmail.com>
Co-Authored-By: Mattias Hagström  <mattiashagstrommusic@gmail.com>
Co-authored-by: Simon Forsberg <simon.co.forsberg@gmail.com>
Co-authored-by: Johan Briger <johanbriger@gmail.com>
Add implementation for AlbumRepository, add implementation for ArtistRepository
Implements methods from SongRepository
JavaFX dependencies
Updated pom.xml-file with
JavaFX dependencies
Added Stylesheet
Updated pom.xml-file with
JavaFX dependencies
Added Stylesheet
Updated pom.xml-file with
JavaFX dependencies
Added Stylesheet
Updated pom.xml-file with
JavaFX dependencies
Added Stylesheet
Updated pom.xml-file with
JavaFX dependencies
MyPod.java(Line 215)
MyPod(Line 276)
@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a complete JavaFX-based music library application with iTunes API integration. It includes JPA entities for managing songs, albums, artists, and playlists; repository implementations for data persistence; an iTunes API client for fetching music data; database configuration via Docker and Maven; and a full GUI featuring an iPod-styled interface with navigation, search, and playlist management capabilities.

Changes

Cohort / File(s) Summary
Build & Dependency Configuration
pom.xml, docker-compose.yml, README.md
Added Maven dependencies (Jackson, JavaFX, maven-compiler-plugin, javafx-maven-plugin) for Java 25 compilation and JavaFX runtime. Added MySQL 9.5.0 Docker service with environment configuration and volume persistence. Added GitHub Classroom deadline badge to README.
Application Entrypoints
src/main/java/org/example/App.java, src/main/java/org/example/MyPod.java
Modified App to launch JavaFX application via Application.launch() instead of printing to console. Added MyPod as main JavaFX Application extending class with iPod-styled UI (screen, click wheel), keyboard navigation (UP/DOWN/ENTER/ESCAPE), and menu system for Songs, Artists, Albums, Playlists. Includes asynchronous data loading via background Task and integration with ItunesPlayList for playlist management.
iTunes API Integration
src/main/java/org/example/ItunesApiClient.java, src/main/java/org/example/ItunesDTO.java
Added ItunesApiClient with searchSongs(String term) method that queries iTunes API with configurable parameters (entity, attribute, limit), handles HTTP timeouts and response validation, and parses JSON into ItunesDTO objects. Added ItunesDTO record with artist/album/track metadata, genre, release date (LocalDate), and convenience releaseYear() method. Uses Jackson with JavaTimeModule for deserialization.
Data Persistence Infrastructure
src/main/java/org/example/PersistenceManager.java, src/main/java/org/example/DatabaseInitializer.java
Added PersistenceManager to configure and expose JPA EntityManagerFactory via ClassGraph entity scanning, MySQL JDBC URL, Hibernate DDL auto, and JVM shutdown hooks. Added DatabaseInitializer to orchestrate data seeding via iTunes API: fetches songs for predefined search terms, creates or reuses Artist/Album/Song entities with idempotency checks (existsByUniqueId), wraps failures in RuntimeException with context.
JPA Entity Models
src/main/java/org/example/entity/Artist.java, src/main/java/org/example/entity/Album.java, src/main/java/org/example/entity/Song.java, src/main/java/org/example/entity/Playlist.java
Added four JPA entities with @Entity annotations, ID generation strategies, and relationships: Artist (1-to-many Album); Album (many-to-one Artist, 1-to-many Song); Song (many-to-one Album, many-to-many Playlist); Playlist (many-to-many Song via join table). Each includes factory methods (fromDTO), constructors, getters/setters, and Hibernate-proxy-aware equals/hashCode. Cascade and orphan removal configured for data consistency.
Repository Interface Layer
src/main/java/org/example/repo/SongRepository.java, src/main/java/org/example/repo/AlbumRepository.java, src/main/java/org/example/repo/ArtistRepository.java, src/main/java/org/example/repo/PlaylistRepository.java
Added four repository interfaces defining contract methods: Song and Album repositories include findByArtist, findByGenre, existsByUniqueId, save, findAll, count; Artist repository includes existsByUniqueId, save, findAll, count; Playlist repository includes CRUD (create, delete, rename) and song management (addSong, removeSong, findSongsInPlaylist).
Repository Implementation Layer
src/main/java/org/example/repo/SongRepositoryImpl.java, src/main/java/org/example/repo/AlbumRepositoryImpl.java, src/main/java/org/example/repo/ArtistRepositoryImpl.java, src/main/java/org/example/repo/PlaylistRepositoryImpl.java
Implemented four repositories using EntityManagerFactory from PersistenceManager. Song/Album/Artist implementations use transactional JPQL queries with eager fetches (JOIN FETCH) for related entities and null safety checks. Playlist implementation provides stub methods (empty returns, no-ops) for future completion. All transactional operations wrapped via emf.runInTransaction/callInTransaction.
UI & Styling
src/main/java/org/example/ItunesPlayList.java, src/main/resources/ipod_style.css
Added ItunesPlayList JavaFX UI class with showLibrary(List<Song>) entry point, internal DisplaySong DTO for table rendering, song table with name/artist/album/duration columns, filterable playlists ("Musik", "Favoriter"), search/filter functionality, LCD display header, context menu for adding songs to playlists, and helper methods for CRUD playlist operations. Added CSS stylesheet with iPod aesthetic (wheel styling, screen panel, LCD elements, gradients, fonts, animations).

Sequence Diagrams

sequenceDiagram
    actor User
    participant App as App.main()
    participant MyPod as MyPod<br/>(JavaFX App)
    participant Task as Background Task
    participant PM as PersistenceManager
    participant DI as DatabaseInitializer
    participant ItunesAPI as iTunes API
    participant Repos as Repositories<br/>(Artist/Album/Song)

    User ->> App: Run application
    App ->> MyPod: Application.launch()
    MyPod ->> MyPod: Create UI (screen, wheel)
    MyPod ->> MyPod: Show main menu
    
    MyPod ->> Task: Start background data load
    
    Task ->> PM: getEntityManagerFactory()
    PM -->> Task: EntityManagerFactory (with MySQL)
    
    Task ->> DI: new DatabaseInitializer(apiClient, repos...)
    Task ->> DI: init()
    
    DI ->> Repos: songRepo.count()
    Repos -->> DI: song count
    
    alt Songs already exist (idempotent)
        DI -->> Task: Early exit
    else First run - seed data
        loop For each search term
            DI ->> ItunesAPI: searchSongs(term)
            ItunesAPI -->> DI: List<ItunesDTO>
            
            loop For each iTunes result
                DI ->> Repos: artistRepo.existsByUniqueId()
                alt Artist not found
                    DI ->> Repos: artistRepo.save(new Artist)
                end
                
                DI ->> Repos: albumRepo.existsByUniqueId()
                alt Album not found
                    DI ->> Repos: albumRepo.save(new Album)
                end
                
                DI ->> Repos: songRepo.existsByUniqueId()
                alt Song not found
                    DI ->> Repos: songRepo.save(new Song)
                end
            end
        end
        DI -->> Task: All data seeded
    end
    
    Task -->> MyPod: Data load complete
    MyPod ->> Repos: Fetch all songs
    Repos -->> MyPod: List<Song>
    MyPod ->> MyPod: Render main menu with data
    MyPod -->> User: Ready for interaction
Loading
sequenceDiagram
    actor User
    participant MyPod as MyPod<br/>(Menu Navigation)
    participant PlaylistUI as ItunesPlayList<br/>(Player Window)
    participant SongRepo as SongRepository
    
    User ->> MyPod: Press ENTER on "Playlists"
    MyPod ->> PlaylistUI: showLibrary(dbSongs)
    PlaylistUI ->> PlaylistUI: initData() - convert to DisplaySong
    PlaylistUI ->> PlaylistUI: setupTable() - songs table
    PlaylistUI ->> PlaylistUI: createPlaylists("Musik", "Favoriter")
    PlaylistUI -->> User: Show player window
    
    loop User interaction
        User ->> PlaylistUI: Select song + right-click
        PlaylistUI ->> PlaylistUI: Context menu - "Add to Musik"
        User ->> PlaylistUI: Click menu item
        PlaylistUI ->> PlaylistUI: addSelectedSong()
        PlaylistUI ->> PlaylistUI: Update active playlist
        PlaylistUI -->> User: Song added (visual update)
    end
    
    User ->> PlaylistUI: Search filter in top bar
    PlaylistUI ->> PlaylistUI: filterSongs(searchText)
    PlaylistUI ->> PlaylistUI: Apply FilteredList predicate
    PlaylistUI -->> User: Filtered results in table
    
    User ->> PlaylistUI: Close window
    PlaylistUI -->> MyPod: Return to menu
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🎵 A rabbit hops through code, with ears held high,
Dancing with iTunes songs beneath the sky,
JPA entities spring to life so bright,
JavaFX wheels spin left and right,
The iPod dreams again—what a delight! 🐰

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'ItunesPlaylist' is vague and generic, using a single noun/class name without descriptive context about the actual changes made in the pull request. Use a more descriptive title that explains the main purpose, such as 'Add iTunes playlist application with JavaFX UI and JPA persistence' or 'Implement music library player with database integration'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Fix all issues with AI Agents 🤖
In @src/main/java/org/example/ItunesApiClient.java:
- Around line 42-44: Replace Swedish comments and error text in ItunesApiClient
with English for consistency: change the comment preceding the
response.statusCode() check ("Kontrollera status") to an English comment like
"Check response status" and update the RuntimeException message ("API-fel: ") to
an English message such as "API error: " plus the status code; likewise convert
the nearby JSON parse comment ("Parse JSON") to "Parse JSON". Locate these
within the method that performs the HTTP request/response handling in class
ItunesApiClient and update the comment strings and exception message
accordingly.

In @src/main/java/org/example/ItunesPlayList.java:
- Around line 336-344: DisplaySong instances are compared by reference when
using contains(), so duplicate detection fails; implement
DisplaySong.equals(Object) and hashCode() to compare the meaningful fields
(name, artist, album, time) so two DisplaySong objects with identical data are
considered equal; update the DisplaySong class (constructor remains same) to
override equals and hashCode using those fields (null-safe) so contains() and
any hashing collections behave correctly.
- Line 199: Replace use of the deprecated resize policy constant: locate the
call songTable.setColumnResizePolicy(...) and change the argument from
TableView.CONSTRAINED_RESIZE_POLICY to
TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS so the TableView uses the
non-deprecated all-columns constrained policy.

In @src/main/java/org/example/repo/PlaylistRepositoryImpl.java:
- Around line 8-44: PlaylistRepositoryImpl currently contains stubs; implement
JPA-backed logic using the PersistenceManager's EntityManagerFactory (emf)
similar to AlbumRepositoryImpl/SongRepositoryImpl: use emf.callInTransaction for
read methods findAll() and findSongsInPlaylist(Playlist) with JPQL queries
against the Playlist entity (e.g., "SELECT p FROM Playlist p" and fetching songs
via JOIN FETCH or retrieving playlist by id), and use emf.runInTransaction for
mutating methods createPlaylist(String), deletePlaylist(Playlist),
addSong(Playlist, Song), removeSong(Playlist, Song), and
renamePlaylist(Playlist, String) to persist/merge/remove entities and properly
manage the many-to-many association between Playlist and Song (update both sides
if necessary and merge the Playlist entity). Ensure you reference
PlaylistRepositoryImpl, PersistenceManager, emf.callInTransaction,
emf.runInTransaction, and the Playlist/Song entities in your changes.
🧹 Nitpick comments (20)
src/main/java/org/example/PersistenceManager.java (1)

16-35: Consider error handling for database unavailability.

If the database is unavailable when this class is loaded, the static initializer will throw an exception that can be difficult to diagnose. The shutdown hook is good for cleanup, but consider wrapping the initialization in a try-catch with a clear error message.

🔎 Suggested improvement
 static {
+    try {
         List<Class<?>> entities = getEntities("org.example.entity");
 
         final PersistenceConfiguration cfg = new HibernatePersistenceConfiguration("emf")
             .jdbcUrl("jdbc:mysql://localhost:3306/myPodDB")
             .jdbcUsername("user")
             .jdbcPassword("pass")
             .property("hibernate.hbm2ddl.auto", "update")
             .property("hibernate.show_sql", "true")
             .property("hibernate.format_sql", "true")
             .property("hibernate.highlight_sql", "true")
             .managedClasses(entities);
 
         emf = cfg.createEntityManagerFactory();
 
-        //From CodeRabbit: Register a shutdown hook to properly release database connections on JVM exit.
         Runtime.getRuntime().addShutdownHook(new Thread(emf::close));
+    } catch (Exception e) {
+        throw new ExceptionInInitializerError("Failed to initialize EntityManagerFactory. Is the database running? " + e.getMessage());
+    }
 }
docker-compose.yml (1)

1-15: Security: Hardcoded credentials in version control.

For a classroom project this is acceptable, but note that production deployments should use environment variables or secrets management. Also, MYSQL_ROOT_HOST: "%" allows root connections from any host, which is a security risk.

pom.xml (1)

75-107: Remove redundant source/target configuration from maven-compiler-plugin.

Since you've set maven.compiler.release=25 in properties, the explicit <source>25</source> and <target>25</target> elements in the plugin configuration are unnecessary. Modern Maven (plugin 3.13.0+) will use the release property automatically. Remove the configuration block from the plugin to keep the pom.xml clean.

src/main/java/org/example/repo/SongRepository.java (1)

20-21: Resolve the uncertainty about findByAlbum method.

The comment indicates uncertainty about whether this method is redundant. If the Album entity maintains a bidirectional relationship with Song (e.g., album.getSongs()), this method might be unnecessary. However, repository methods can still provide value for querying without loading the full entity graph.

Do you want me to:

  1. Check if Album has a collection of songs that would make this redundant?
  2. Help you decide based on your query patterns and performance needs?
src/main/java/org/example/ItunesApiClient.java (1)

31-31: Consider making the result limit configurable.

The hardcoded limit=8 reduces flexibility. Consider making this a parameter or class-level configuration to support different use cases.

🔎 Example refactor
-    public List<ItunesDTO> searchSongs(String term) throws Exception {
+    public List<ItunesDTO> searchSongs(String term, int limit) throws Exception {
 
         String encodedTerm = URLEncoder.encode(term, StandardCharsets.UTF_8);
-        String url = "https://itunes.apple.com/search?term=" + encodedTerm + "&entity=song&attribute=artistTerm&limit=8";
+        String url = "https://itunes.apple.com/search?term=" + encodedTerm + "&entity=song&attribute=artistTerm&limit=" + limit;

Or add a default method:

public List<ItunesDTO> searchSongs(String term) throws Exception {
    return searchSongs(term, 8);
}
src/main/java/org/example/DatabaseInitializer.java (1)

44-61: Consider wrapping entity creation in a single transaction per DTO.

Each Artist, Album, and Song is persisted in separate transactions with individual existsByUniqueId checks. While this provides idempotency, it lacks atomicity for the logical unit (one DTO → one complete Artist/Album/Song graph).

If an exception occurs mid-processing, you may end up with an Artist and Album persisted but no Song. For more robust initialization, consider wrapping the entire entity graph creation for each DTO in a single transaction.

💡 Example approach
apiClient.searchSongs(term).forEach(dto -> {
    // Wrap the entire DTO processing in one transaction
    emf.runInTransaction(em -> {
        Artist ar = Artist.fromDTO(dto);
        if (!artistRepo.existsByUniqueId(ar)) {
            artistRepo.save(ar);
        }
        
        Album al = Album.fromDTO(dto, ar);
        if (!albumRepo.existsByUniqueId(al)) {
            albumRepo.save(al);
        }
        
        Song s = Song.fromDTO(dto, al);
        if (!songRepo.existsByUniqueId(s)) {
            songRepo.save(s);
        }
    });
});

Note: This assumes PersistenceManager's EntityManagerFactory exposes runInTransaction.

src/main/java/org/example/repo/SongRepositoryImpl.java (1)

18-32: Use transactional helpers consistently with other repository implementations.

The count() and existsByUniqueId() methods use try-with-resources, while all other methods in this class use emf.callInTransaction or emf.runInTransaction. This is inconsistent with AlbumRepositoryImpl (lines 14-21, 53-58), which uses the transactional helpers for all operations including count and existence checks.

Using the transactional helpers ensures consistent transaction management across all repository operations.

🔎 Proposed refactor for consistency
 @Override
 public Long count() {
-    try (var em = emf.createEntityManager()) {
-        return em.createQuery("select count(s) from Song s", Long.class)
-            .getSingleResult();
-    }
+    return emf.callInTransaction(em ->
+        em.createQuery("select count(s) from Song s", Long.class)
+            .getSingleResult());
 }

 @Override
 public boolean existsByUniqueId(Song song) {
-    try (var em = emf.createEntityManager()) {
-        return em.createQuery("select count(s) from Song s where s.songId = :songId", Long.class)
-            .setParameter("songId", song.getSongId())
-            .getSingleResult() > 0;
-    }
+    return emf.callInTransaction(em ->
+        em.createQuery("select count(s) from Song s where s.songId = :songId", Long.class)
+            .setParameter("songId", song.getSongId())
+            .getSingleResult() > 0);
 }
src/main/java/org/example/entity/Playlist.java (1)

19-28: Missing getter for songs collection and incomplete bidirectional synchronization.

  1. There's no getSongs() method, which will prevent accessing the songs in a playlist from other layers.

  2. Since Song has a @ManyToMany(mappedBy = "songs") relationship back to Playlist, addSong/removeSong should synchronize both sides to avoid inconsistency when entities are detached or when navigating from the Song side.

🔎 Proposed fix
+    public Set<Song> getSongs() {
+        return songs;
+    }
+
     public void addSong(Song song) {
         this.songs.add(song);
+        song.getPlaylist().add(this);
     }

     public void removeSong(Song song) {
         this.songs.remove(song);
+        song.getPlaylist().remove(this);
     }
src/main/java/org/example/entity/Album.java (3)

7-7: Unused import.

java.time.LocalDate is imported but never used in this file.

-import java.time.LocalDate;

27-32: Remove commented-out code.

Dead code clutters the codebase and reduces readability. If these alternatives are no longer needed, remove them.

-//    @OneToMany(mappedBy = "album")
-    //private List<Song> song;
-
-   // @ManyToOne(cascade = CascadeType.PERSIST)
-   // @JoinColumn(name="artist_id")
- //   private Artist artist;
-

38-39: Consider renaming song to songs for clarity.

Collection fields are conventionally named in plural form. This applies to the getter/setter as well (getSongs()/setSongs()).

src/main/java/org/example/entity/Artist.java (1)

22-23: Consider renaming album to albums for clarity.

Collection fields are conventionally named in plural form, consistent with the suggested change in Album.java.

src/main/java/org/example/entity/Song.java (2)

7-7: Unused import.

java.time.LocalDate is imported but never used.

-import java.time.LocalDate;

27-28: Consider renaming playlist to playlists for clarity.

This collection field represents multiple playlists, so a plural name would be more accurate.

src/main/java/org/example/MyPod.java (4)

360-373: Unused variable and error handling concern.

The emf variable on line 362 is fetched but never used. If it's meant to ensure the EntityManagerFactory is initialized, consider making that intent explicit.

Also, the catch block silently logs to stderr but the calling Task won't trigger onFailed since the exception is caught. Consider rethrowing or using a different error handling strategy.

🔎 Proposed fix
     private void initializeData() {
-        try {
-            EntityManagerFactory emf = PersistenceManager.getEntityManagerFactory();
+        try {
+            // Ensure EMF is initialized (side effect of static initializer)
+            PersistenceManager.getEntityManagerFactory();
             DatabaseInitializer initializer = new DatabaseInitializer(apiClient, songRepo, albumRepo, artistRepo);
             initializer.init(); // Fyll databasen om den är tom

             // Hämta data till minnet
             this.songs = songRepo.findAll();
             this.artists = artistRepo.findAll();
             this.albums = albumRepo.findAll();
         } catch (Exception e) {
             System.err.println("Kunde inte ladda data: " + e.getMessage());
+            throw new RuntimeException("Data initialization failed", e);
         }
     }

105-107: Background thread should be a daemon thread.

If the application exits while this thread is running, it could prevent JVM shutdown. Mark it as a daemon thread to ensure clean exit.

🔎 Proposed fix
-        new Thread(initTask).start();
+        Thread initThread = new Thread(initTask);
+        initThread.setDaemon(true);
+        initThread.start();

111-116: Potential NullPointerException when loading CSS.

If getResource("/ipod_style.css") returns null, calling toExternalForm() will throw an NPE, which is caught but the cause is obscured. A null check would provide clearer error messaging.

🔎 Proposed fix
         try {
-            // Försök ladda CSS-filen för styling
-            scene.getStylesheets().add(getClass().getResource("/ipod_style.css").toExternalForm());
+            var cssUrl = getClass().getResource("/ipod_style.css");
+            if (cssUrl != null) {
+                scene.getStylesheets().add(cssUrl.toExternalForm());
+            } else {
+                System.out.println("CSS hittades inte, kör utan styling.");
+            }
         } catch (Exception e) {
             System.out.println("CSS hittades inte, kör utan styling.");
         }

351-354: Selecting "Playlists" repeatedly opens multiple windows.

Each selection of "Playlists" creates a new ItunesPlayList instance and shows a new Stage. Consider tracking the window and reusing/focusing it if already open.

src/main/java/org/example/ItunesPlayList.java (2)

213-236: Creating a new FilteredList on every keystroke is inefficient.

Consider creating a single FilteredList per playlist and updating its predicate instead of constructing a new one each time.

🔎 Suggested approach
-    private void filterSongs(String searchText) {
-        String currentList = sourceList.getSelectionModel().getSelectedItem();
-        if (currentList == null) return;
-
-        ObservableList<DisplaySong> masterData = allPlaylists.get(currentList);
-
-        if (searchText == null || searchText.isEmpty()) {
-            songTable.setItems(masterData);
-            return;
-        }
-
-        FilteredList<DisplaySong> filteredData = new FilteredList<>(masterData, song -> {
-            String filter = searchText.toLowerCase();
-            return song.name.toLowerCase().contains(filter) ||
-                song.artist.toLowerCase().contains(filter) ||
-                song.album.toLowerCase().contains(filter);
-        });
-
-        songTable.setItems(filteredData);
-    }
+    // Store filtered list at class level and update predicate instead
+    private FilteredList<DisplaySong> currentFilteredList;
+
+    private void filterSongs(String searchText) {
+        if (currentFilteredList == null) return;
+        
+        if (searchText == null || searchText.isEmpty()) {
+            currentFilteredList.setPredicate(null);
+        } else {
+            String filter = searchText.toLowerCase();
+            currentFilteredList.setPredicate(song ->
+                song.name.toLowerCase().contains(filter) ||
+                song.artist.toLowerCase().contains(filter) ||
+                song.album.toLowerCase().contains(filter));
+        }
+    }

Then initialize currentFilteredList when playlist selection changes.


141-143: Potential NullPointerException when loading CSS.

Same issue as in MyPod.java - if getResource() returns null, this will throw an NPE.

🔎 Proposed fix
-        scene.getStylesheets().add(getClass().getResource("/ipod_style.css").toExternalForm());
+        var cssUrl = getClass().getResource("/ipod_style.css");
+        if (cssUrl != null) {
+            scene.getStylesheets().add(cssUrl.toExternalForm());
+        }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6651b37 and 9cdff2a.

📒 Files selected for processing (23)
  • README.md
  • docker-compose.yml
  • pom.xml
  • src/main/java/org/example/App.java
  • src/main/java/org/example/DatabaseInitializer.java
  • src/main/java/org/example/ItunesApiClient.java
  • src/main/java/org/example/ItunesDTO.java
  • src/main/java/org/example/ItunesPlayList.java
  • src/main/java/org/example/MyPod.java
  • src/main/java/org/example/PersistenceManager.java
  • src/main/java/org/example/entity/Album.java
  • src/main/java/org/example/entity/Artist.java
  • src/main/java/org/example/entity/Playlist.java
  • src/main/java/org/example/entity/Song.java
  • src/main/java/org/example/repo/AlbumRepository.java
  • src/main/java/org/example/repo/AlbumRepositoryImpl.java
  • src/main/java/org/example/repo/ArtistRepository.java
  • src/main/java/org/example/repo/ArtistRepositoryImpl.java
  • src/main/java/org/example/repo/PlaylistRepository.java
  • src/main/java/org/example/repo/PlaylistRepositoryImpl.java
  • src/main/java/org/example/repo/SongRepository.java
  • src/main/java/org/example/repo/SongRepositoryImpl.java
  • src/main/resources/ipod_style.css
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/org/example/MyPod.java (1)
src/main/java/org/example/PersistenceManager.java (1)
  • PersistenceManager (12-54)
src/main/java/org/example/PersistenceManager.java (4)
src/main/java/org/example/entity/Album.java (1)
  • Entity (12-130)
src/main/java/org/example/entity/Artist.java (1)
  • Entity (11-88)
src/main/java/org/example/entity/Song.java (1)
  • Entity (12-111)
src/main/java/org/example/entity/Playlist.java (1)
  • Entity (10-61)
src/main/java/org/example/repo/ArtistRepositoryImpl.java (1)
src/main/java/org/example/PersistenceManager.java (1)
  • PersistenceManager (12-54)
src/main/java/org/example/repo/SongRepositoryImpl.java (1)
src/main/java/org/example/PersistenceManager.java (1)
  • PersistenceManager (12-54)
src/main/java/org/example/entity/Song.java (3)
src/main/java/org/example/entity/Album.java (1)
  • Entity (12-130)
src/main/java/org/example/entity/Artist.java (1)
  • Entity (11-88)
src/main/java/org/example/entity/Playlist.java (1)
  • Entity (10-61)
🪛 Biome (2.1.2)
src/main/resources/ipod_style.css

[error] 3-3: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 8-8: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 9-9: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 10-10: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 11-11: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 12-12: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 16-16: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 17-17: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 18-18: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 19-19: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 20-20: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 21-21: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 25-25: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 26-26: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 27-27: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 28-28: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 32-32: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 33-33: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 38-38: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 39-39: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 43-43: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 44-44: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 48-48: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 49-49: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 50-50: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 54-54: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 54-54: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 55-55: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 55-55: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 56-56: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 56-56: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 57-57: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 57-57: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 61-61: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 62-62: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 63-63: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 67-67: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 68-68: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 69-69: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 70-70: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 74-74: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 75-75: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 76-76: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 77-77: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 81-81: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 82-82: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 83-83: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 87-87: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 89-89: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 90-90: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 93-94: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 96-96: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 97-97: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 99-99: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 100-100: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 102-103: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 103-104: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 104-104: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 109-109: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 110-110: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 111-112: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 112-113: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 115-115: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 115-115: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 116-116: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 116-116: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 120-120: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 123-123: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 124-124: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)

🔇 Additional comments (23)
README.md (1)

1-1: LGTM!

The GitHub Classroom deadline badge is a useful addition for tracking assignment deadlines.

src/main/resources/ipod_style.css (1)

1-125: LGTM! Static analysis warnings are false positives.

The Biome linter is flagging -fx-* properties as unknown because it's designed for web CSS. These are valid JavaFX CSS properties that follow JavaFX's CSS syntax (e.g., -fx-background-color, -fx-fill, -fx-font-family). The stylesheet correctly implements an iPod-inspired theme for the JavaFX UI.

src/main/java/org/example/PersistenceManager.java (1)

41-53: LGTM!

The entity scanning using ClassGraph with try-with-resources is well implemented. The ScanResult is properly closed after use.

src/main/java/org/example/App.java (1)

3-9: LGTM!

Clean entry point that correctly delegates to the JavaFX application. Using Application.launch(MyPod.class, args) is the proper way to start a JavaFX application from a non-Application class.

pom.xml (1)

48-72: LGTM!

Jackson and JavaFX dependencies are properly added with matching versions. The jackson-datatype-jsr310 module is essential for handling Java 8+ date/time types with the iTunes API response.

src/main/java/org/example/repo/ArtistRepository.java (1)

7-17: LGTM!

Clean repository interface following the repository pattern. The method signatures are appropriate for the use case.

One optional consideration: existsByUniqueId(Artist artist) could be simplified to existsByUniqueId(Long artistId) since only the ID is needed to check existence. However, the current signature works well with the Artist.fromDTO() factory method pattern used in DatabaseInitializer.

src/main/java/org/example/ItunesDTO.java (1)

7-23: LGTM! Clean DTO design.

The record structure with @JsonIgnoreProperties(ignoreUnknown = true) provides good resilience against API changes. The releaseYear() convenience method appropriately handles null values.

src/main/java/org/example/repo/AlbumRepository.java (1)

8-21: LGTM! Well-structured repository interface.

The interface provides a clear contract for Album data access operations with appropriate query methods for common use cases (by artist, by genre).

src/main/java/org/example/repo/ArtistRepositoryImpl.java (3)

15-22: LGTM! Proper existence check implementation.

The existsByUniqueId method correctly uses a count query to check for existing artists by their iTunes API identifier.


29-41: LGTM! Standard query implementations.

The findAll() and count() methods follow standard JPA patterns with proper transaction handling.


24-27: No changes needed; the current implementation is correct.

The save() method using persist() is appropriate for this codebase. The code consistently follows a defensive pattern where every call to save() is guarded by an existsByUniqueId() check that prevents persisting entities that already exist. Entities passed to save() are always transient (freshly created from DTOs), making persist() the right choice. No refactoring to merge(), separate create()/update() methods, or renaming is necessary.

Likely an incorrect or invalid review comment.

src/main/java/org/example/ItunesApiClient.java (2)

22-26: LGTM! Proper Jackson configuration.

The ObjectMapper is correctly configured with JavaTimeModule to handle LocalDate deserialization from the iTunes API.


47-58: LGTM! Robust JSON parsing.

The code properly validates the JSON structure before mapping and gracefully handles missing or malformed results by returning an empty list.

src/main/java/org/example/repo/AlbumRepositoryImpl.java (1)

10-58: LGTM! Clean and consistent repository implementation.

The implementation follows a consistent pattern with proper transaction management using the emf.callInTransaction and emf.runInTransaction helpers. The JPQL queries are straightforward and appropriate for the repository operations.

src/main/java/org/example/DatabaseInitializer.java (1)

27-30: LGTM! Good idempotency check.

The early exit when data already exists prevents duplicate initialization and makes this method safe to call multiple times.

src/main/java/org/example/repo/PlaylistRepository.java (1)

8-23: LGTM! Clean repository interface.

The interface defines a clear contract for playlist operations with appropriate method signatures.

src/main/java/org/example/repo/SongRepositoryImpl.java (1)

48-103: LGTM! Proper null checks and efficient fetch joins.

The query methods include appropriate null/blank guards and use join fetch to eagerly load related entities, which will prevent N+1 query issues when accessing Album and Artist data from Song results.

src/main/java/org/example/entity/Playlist.java (1)

46-60: Proxy-aware equals/hashCode implementation looks correct.

The implementation properly handles Hibernate proxies and uses identity-based equality with the persistence ID. The constant hashCode() based on class identity is appropriate for JPA entities to maintain hash consistency across entity state transitions.

src/main/java/org/example/entity/Album.java (1)

52-57: Factory method implementation is clean.

The fromDTO method properly validates required fields before construction and provides a clear error message. This is a good pattern for DTO-to-entity conversion.

src/main/java/org/example/entity/Artist.java (1)

34-39: Factory method and entity structure look good.

The fromDTO method properly validates required fields. The entity structure with proxy-aware equals/hashCode is consistent with the other entities in this PR.

src/main/java/org/example/entity/Song.java (1)

40-45: Factory method and entity structure are well implemented.

The fromDTO validation and construction pattern is consistent with Album and Artist. The toString() implementation is a nice addition for debugging.

src/main/java/org/example/MyPod.java (1)

63-126: Overall JavaFX application structure is well organized.

The use of Task for background data loading, proper UI thread handling with Platform.runLater, and clear separation of UI construction methods demonstrate good JavaFX practices.

src/main/java/org/example/ItunesPlayList.java (1)

49-148: Overall GUI implementation is functional and well-structured.

The separation of concerns with initData, setupTable, and action handlers is clean. The use of ObservableList and listeners follows JavaFX idioms appropriately.

Comment on lines +42 to +44
// Kontrollera status
if (response.statusCode() != 200) {
throw new RuntimeException("API-fel: " + response.statusCode());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove Swedish comments and error messages for consistency.

The codebase uses English for code and comments elsewhere, but this file contains Swedish comments ("Kontrollera status", "Parse JSON") and error messages ("API-fel: "). This creates inconsistency and may confuse international contributors.

🔎 Proposed fix
-        // Kontrollera status
         if (response.statusCode() != 200) {
-            throw new RuntimeException("API-fel: " + response.statusCode());
+            throw new RuntimeException("iTunes API error: " + response.statusCode());
         }
 
-        // Parse JSON
         JsonNode root = mapper.readTree(response.body());

Also applies to: 47-47

🤖 Prompt for AI Agents
In @src/main/java/org/example/ItunesApiClient.java around lines 42-44, Replace
Swedish comments and error text in ItunesApiClient with English for consistency:
change the comment preceding the response.statusCode() check ("Kontrollera
status") to an English comment like "Check response status" and update the
RuntimeException message ("API-fel: ") to an English message such as "API error:
" plus the status code; likewise convert the nearby JSON parse comment ("Parse
JSON") to "Parse JSON". Locate these within the method that performs the HTTP
request/response handling in class ItunesApiClient and update the comment
strings and exception message accordingly.

songTable.getStyleClass().add("song-table");

// Gör så att kolumnerna fyller ut hela bredden
songTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

JavaFX 20 TableView CONSTRAINED_RESIZE_POLICY deprecated replacement CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS

💡 Result:

Yes. In JavaFX 20 the old TableView.CONSTRAINED_RESIZE_POLICY was deprecated and replaced by specific constrained policies — e.g. TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS (and also CONSTRAINED_RESIZE_POLICY_LAST_COLUMN, CONSTRAINED_RESIZE_POLICY_NEXT_COLUMN, CONSTRAINED_RESIZE_POLICY_SUBSEQUENT_COLUMNS). See the JavaFX 20 TableView API and the deprecated list. [1][2]


Replace deprecated TableView resize policy.

TableView.CONSTRAINED_RESIZE_POLICY is deprecated since JavaFX 20. Use TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS instead.

🤖 Prompt for AI Agents
In @src/main/java/org/example/ItunesPlayList.java around line 199, Replace use
of the deprecated resize policy constant: locate the call
songTable.setColumnResizePolicy(...) and change the argument from
TableView.CONSTRAINED_RESIZE_POLICY to
TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS so the TableView uses the
non-deprecated all-columns constrained policy.

Comment on lines +336 to +344
public static class DisplaySong {
String name, artist, album, time;
public DisplaySong(String n, String a, String al, Long t) {
this.name = n;
this.artist = a;
this.album = al;
this.time = String.valueOf(t);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

DisplaySong lacks equals/hashCode, breaking duplicate detection.

The contains() check on line 322 relies on Object.equals(), which compares by reference. Two DisplaySong objects with identical data will not be detected as duplicates, allowing the same song to be added multiple times to a playlist.

🔎 Proposed fix
     public static class DisplaySong {
         String name, artist, album, time;
         public DisplaySong(String n, String a, String al, Long t) {
             this.name = n;
             this.artist = a;
             this.album = al;
             this.time = String.valueOf(t);
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            DisplaySong that = (DisplaySong) o;
+            return java.util.Objects.equals(name, that.name) &&
+                   java.util.Objects.equals(artist, that.artist) &&
+                   java.util.Objects.equals(album, that.album);
+        }
+
+        @Override
+        public int hashCode() {
+            return java.util.Objects.hash(name, artist, album);
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static class DisplaySong {
String name, artist, album, time;
public DisplaySong(String n, String a, String al, Long t) {
this.name = n;
this.artist = a;
this.album = al;
this.time = String.valueOf(t);
}
}
public static class DisplaySong {
String name, artist, album, time;
public DisplaySong(String n, String a, String al, Long t) {
this.name = n;
this.artist = a;
this.album = al;
this.time = String.valueOf(t);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DisplaySong that = (DisplaySong) o;
return java.util.Objects.equals(name, that.name) &&
java.util.Objects.equals(artist, that.artist) &&
java.util.Objects.equals(album, that.album);
}
@Override
public int hashCode() {
return java.util.Objects.hash(name, artist, album);
}
}
🤖 Prompt for AI Agents
In @src/main/java/org/example/ItunesPlayList.java around lines 336-344,
DisplaySong instances are compared by reference when using contains(), so
duplicate detection fails; implement DisplaySong.equals(Object) and hashCode()
to compare the meaningful fields (name, artist, album, time) so two DisplaySong
objects with identical data are considered equal; update the DisplaySong class
(constructor remains same) to override equals and hashCode using those fields
(null-safe) so contains() and any hashing collections behave correctly.

Comment on lines +8 to +44
public class PlaylistRepositoryImpl implements PlaylistRepository {

@Override
public List<Playlist> findAll() {
return List.of();
}

@Override
public List<Song> findSongsInPlaylist(Playlist playlist) {
return List.of();
}

@Override
public void createPlaylist(String name) {

}

@Override
public void deletePlaylist(Playlist playlist) {

}

@Override
public void addSong(Playlist playlist, Song song) {

}

@Override
public void removeSong(Playlist playlist, Song song) {

}

@Override
public void renamePlaylist(Playlist playlist, String newName) {

}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement the playlist repository methods.

All methods in this repository are stubs or return empty collections, rendering the entire playlist feature non-functional. Users won't be able to create, view, modify, or delete playlists.

This should be implemented with JPA-based persistence similar to the other repository implementations (e.g., AlbumRepositoryImpl, SongRepositoryImpl) using the EntityManagerFactory from PersistenceManager.

Do you want me to generate a complete implementation that follows the same patterns as the other repositories in this PR?

💡 Expected implementation pattern

The implementation should:

  1. Obtain EntityManagerFactory from PersistenceManager
  2. Use emf.callInTransaction for queries (findAll, findSongsInPlaylist)
  3. Use emf.runInTransaction for modifications (create, delete, add/remove songs, rename)
  4. Use JPQL queries to interact with the Playlist entity
  5. Handle the many-to-many relationship between Playlist and Song properly
🤖 Prompt for AI Agents
In @src/main/java/org/example/repo/PlaylistRepositoryImpl.java around lines
8-44, PlaylistRepositoryImpl currently contains stubs; implement JPA-backed
logic using the PersistenceManager's EntityManagerFactory (emf) similar to
AlbumRepositoryImpl/SongRepositoryImpl: use emf.callInTransaction for read
methods findAll() and findSongsInPlaylist(Playlist) with JPQL queries against
the Playlist entity (e.g., "SELECT p FROM Playlist p" and fetching songs via
JOIN FETCH or retrieving playlist by id), and use emf.runInTransaction for
mutating methods createPlaylist(String), deletePlaylist(Playlist),
addSong(Playlist, Song), removeSong(Playlist, Song), and
renamePlaylist(Playlist, String) to persist/merge/remove entities and properly
manage the many-to-many association between Playlist and Song (update both sides
if necessary and merge the Playlist entity). Ensure you reference
PlaylistRepositoryImpl, PersistenceManager, emf.callInTransaction,
emf.runInTransaction, and the Playlist/Song entities in your changes.

@johanbriger johanbriger closed this Jan 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants