Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/main/java/com/CDPrintable/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ public class Constants {
public static final String VERSION = "1.6.4";

public static final boolean USER_AGENT_EMAIL_CHANGED = false;
}

public static final int MAX_THREADS = 4;
public static final ThreadManager THREAD_MANAGER = new ThreadManager();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@

package com.CDPrintable.MusicBrainzResources;

import com.CDPrintable.Constants;
import com.google.gson.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.lang.reflect.Array;
import java.util.concurrent.Future;

public class MusicBrainzJSONReader {
private final JsonObject json;
Expand All @@ -36,6 +41,28 @@ public MusicBrainzJSONReader(String json) throws IllegalArgumentException {
this.json = tempJsonObject;
}

/**
* This method splits a JSON array into smaller chunks for multithreading.
* @param jsonArray The JSON array to split.
*/
public JsonArray[] splitJsonArray(JsonArray jsonArray) {
int chunkSize = Constants.MAX_THREADS;
int arraySize = jsonArray.size();
int numChunks = (int) Math.ceil((double) arraySize / chunkSize);
JsonArray[] chunks = new JsonArray[numChunks];

for (int i = 0; i < numChunks; i++) {
int start = i * chunkSize;
int end = Math.min(start + chunkSize, arraySize);
JsonArray chunk = new JsonArray();
for (int j = start; j < end; j++) {
chunk.add(jsonArray.get(j));
}
chunks[i] = chunk;
}
return chunks;
}

/**
* Parses a JSON array and creates a new array of the same type as the provided array.
*
Expand All @@ -49,22 +76,40 @@ public MusicBrainzJSONReader(String json) throws IllegalArgumentException {
*/
@SuppressWarnings("unchecked")
private <T> T[] parseJsonArray(String key, JsonArrayProcessor<T> processor, T[] array) {
// Make sure the key exists in the JSON object
if (!json.has(key)) {
// Return an empty array if the JSON object does not have the key
array = (T[]) Array.newInstance(array.getClass().getComponentType(), 0);
return array;
if(!json.has(key)) {
// Return an empty array if the key does not exist
return (T[]) Array.newInstance(array.getClass().getComponentType(), 0);
}
// Make a JSON array using the provided key

JsonArray jsonArray = json.getAsJsonArray(key);
array = (T[]) Array.newInstance(array.getClass().getComponentType(), jsonArray.size());
JsonArray[] chunks = splitJsonArray(jsonArray);

// Process each JSON object in the array
for (int i = 0; i < jsonArray.size(); i++) {
JsonObject jsonObject = jsonArray.get(i).getAsJsonObject();
array[i] = processor.process(jsonObject); // Uses provided processor to process the JSON object
// Use multithreading to process the JSON array
// This is a list of promises that promise to return a list
// of whatever type T is, e.g., MusicBrainzRelease.
List<Future<List<T>>> futures = new ArrayList<>();
for (JsonArray chunk : chunks) {
// Submit a task to the thread manager
futures.add(Constants.THREAD_MANAGER.submit(() -> {
List<T> result = new ArrayList<>();
for (JsonElement element : chunk) {
JsonObject jsonObject = element.getAsJsonObject();
result.add(processor.process(jsonObject));
}
return result;
}));
}
return array;

List<T> resultList = new ArrayList<>();
try {
for (Future<List<T>> future : futures) {
resultList.addAll(future.get());
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}

return resultList.toArray((T[]) Array.newInstance(array.getClass().getComponentType(), resultList.size()));
}

/**
Expand Down
56 changes: 36 additions & 20 deletions src/main/java/com/CDPrintable/ProgramWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
public class ProgramWindow {
private final UserAgent userAgent;
private JLabel fullUserAgentLabel = new JLabel();
private final JPanel cdSearchPanel = new JPanel();
private final JLabel searchStatusLabel = new JLabel("Status: Nothing's going on.");

/**
* Creates a new ProgramWindow and sets up the GUI.
Expand Down Expand Up @@ -76,20 +78,20 @@ private JPanel searchPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());

// Track List panel set up
// Track List panel set-up
JPanel trackListPanel = new JPanel(new BorderLayout());
trackListPanel.setBorder(BorderFactory.createTitledBorder("Search Results"));

// Search table set up
JTable searchTable = new JTable(getCDStubModel());
JScrollPane trackListScrollPane = new JScrollPane(searchTable);
trackListPanel.add(searchStatusLabel, BorderLayout.NORTH);
trackListPanel.add(trackListScrollPane, BorderLayout.CENTER);

// Add the Track List panel to the main panel
panel.add(trackListPanel, BorderLayout.CENTER);

// CD Search Panel set up
JPanel cdSearchPanel = new JPanel();
cdSearchPanel.setBorder(BorderFactory.createTitledBorder("Search"));

JTextField searchField = new JTextField(15);
Expand All @@ -102,26 +104,36 @@ private JPanel searchPanel() {
if (searchTypeComboBox.getSelectedItem() == null) {
return;
}
setSearchStatus("Preforming search...", "blue");
if (searchTypeComboBox.getSelectedItem().equals("CDStub")) {
MusicBrainzJSONReader reader = sendRequest("cdstub", searchField.getText());

// Get CDStubs and set the table model
MusicBrainzCDStub[] cdStubs = reader.getCDStubs();
searchTable.setModel(reader.getCDStubsAsTableModel(cdStubs));
Constants.THREAD_MANAGER.submit(() -> {
MusicBrainzJSONReader reader = sendRequest("cdstub", searchField.getText());

// Get CDStubs and set the table model
MusicBrainzCDStub[] cdStubs = reader.getCDStubs();
searchTable.setModel(reader.getCDStubsAsTableModel(cdStubs));
setSearchStatus("All done!", "green");
});
} else if (searchTypeComboBox.getSelectedItem().equals("Artist")) {
MusicBrainzJSONReader reader = sendRequest("artist", searchField.getText());

// Get Artists and set the table model
MusicBrainzArtist[] artists = reader.getArtists();
searchTable.setModel(reader.getArtistsAsTableModel(artists));
Constants.THREAD_MANAGER.submit(() -> {
MusicBrainzJSONReader reader = sendRequest("artist", searchField.getText());

// Get Artists and set the table model
MusicBrainzArtist[] artists = reader.getArtists();
searchTable.setModel(reader.getArtistsAsTableModel(artists));
setSearchStatus("All done!", "green");
});
} else if (searchTypeComboBox.getSelectedItem().equals("Release")) {
MusicBrainzJSONReader reader = sendRequest("release", searchField.getText());

// Get Releases and set the table model
MusicBrainzRelease[] releases = reader.getReleases();
searchTable.setModel(reader.getReleasesAsTableModel(releases));
Constants.THREAD_MANAGER.submit(() -> {
MusicBrainzJSONReader reader = sendRequest("release", searchField.getText());

// Get Releases and set the table model
MusicBrainzRelease[] releases = reader.getReleases();
searchTable.setModel(reader.getReleasesAsTableModel(releases));
setSearchStatus("All done!", "green");
});
} else {
// how does this even happen
// how does this even happen?
JOptionPane.showMessageDialog(panel, "Please select a search type.");
}
resizeColumnWidths(searchTable);
Expand Down Expand Up @@ -286,15 +298,15 @@ public void changedUpdate(DocumentEvent e) {} // Not used
userAgentPanel.add(fullAgentPanel, BorderLayout.NORTH);
userAgentPanel.add(userAgentInputPanel, BorderLayout.CENTER);

// Add sub panels to main panel
// Add subpanels to the main panel
panel.add(userAgentPanel);
panel.add(fontPanel);

return panel;
}

/**
* Helper method to resize a tables columns to fit the largest element.
* Helper method to resize a table column to fit the largest element.
* @param table The table to resize.
*/
private void resizeColumnWidths(JTable table) {
Expand All @@ -313,4 +325,8 @@ private void resizeColumnWidths(JTable table) {
tableColumn.setPreferredWidth(preferredWidth + 2); // Add padding
}
}

public void setSearchStatus(String status, String color) {
searchStatusLabel.setText("<html><font color='" + color + "'>Status: " + status + "</font></html>");
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/CDPrintable/ThreadManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* CDPrintable: A program that prints labels with track listings for your CD cases.
* Copyright (C) 2025 Alexander McLean
*
* This source code is licensed under the GNU General Public License v3.0
* found in the LICENSE file in the root directory of this source tree.
*
* This class manages threads.
*/

package com.CDPrintable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadManager {
private final ExecutorService executorService;

public ThreadManager() {
executorService = Executors.newFixedThreadPool(Constants.MAX_THREADS);
}

public <T> Future<T> submit(Callable<T> task) {
return executorService.submit(task);
}

public void submit(Runnable task) {
executorService.submit(task);
}
}