diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java index 6fdf3113..346d5445 100644 --- a/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/Component/ComponentExt.java @@ -8,9 +8,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @Extension +@NullMarked public class ComponentExt { private ComponentExt() { @@ -21,7 +23,7 @@ public static void setRecursive(@This Component component, Consumer c setRecursive(component, consumer, _ -> true); } - public static void setRecursive(@This Component component, Consumer consumer, + public static void setRecursive(@This @Nullable Component component, Consumer consumer, Predicate condition) { if (component != null) { consumer.accept(component); diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java index 7f094536..18162cd2 100644 --- a/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/Container/ContainerExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class ContainerExt { private ContainerExt() { // hide utility class constructor diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java index c2c4e90e..75bbf5dc 100644 --- a/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagConstraints/GridBagConstraintsExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class GridBagConstraintsExt { private GridBagConstraintsExt() { // hide utility class constructor diff --git a/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java index e43fdde9..a3330279 100644 --- a/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java +++ b/MultiSubDownloader/src/main/java/extensions/java/awt/GridBagLayout/GridBagLayoutExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class GridBagLayoutExt { private GridBagLayoutExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java index 7b5390ba..540a2c2f 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/AbstractButton/AbstractButtonExt.java @@ -8,9 +8,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; @Extension +@NullMarked public class AbstractButtonExt { private AbstractButtonExt() { // hide utility class constructor diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java index ab091523..a1249a60 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JButton/JButtonExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JButtonExt { private JButtonExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java index 6835983d..6264e11f 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JCheckBox/JCheckBoxExt.java @@ -5,9 +5,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; @Extension +@NullMarked public class JCheckBoxExt { private JCheckBoxExt() { // hide utility class constructor diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java index d76d618e..1db7cb60 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComboBox/JComboBoxExt.java @@ -11,16 +11,19 @@ import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; import manifold.ext.rt.api.ThisClass; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.gui.ToStringListCellRenderer; @Extension +@NullMarked public class JComboBoxExt { private JComboBoxExt() { // hide utility class constructor } + public static JComboBox create(@ThisClass Class> thisClass, E... values) { return new JComboBox<>(values); } diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java index 4d0a2103..5f66e949 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JComponent/JComponentExt.java @@ -8,9 +8,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @Extension +@NullMarked public class JComponentExt { private JComponentExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java index 3dc000e7..c6c6b2ca 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JProgressBar/JProgressBarExt.java @@ -5,9 +5,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JProgressBarExt { private JProgressBarExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java index 46dcf993..98d6093c 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JScrollPane/JScrollPaneExt.java @@ -6,8 +6,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JScrollPaneExt { private JScrollPaneExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java index 99c1e731..d3b83223 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JSlider/JSliderExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JSliderExt { private JSliderExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java index fa6d9f0f..947c19c0 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTabbedPane/TabbedPaneExt.java @@ -7,8 +7,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class TabbedPaneExt { private TabbedPaneExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java index 5e3200f5..d74b792d 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTable/JTableExt.java @@ -7,8 +7,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JTableExt { private JTableExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java index fdc8d163..9f01b39a 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextArea/JTextAreaExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JTextAreaExt { private JTextAreaExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java index 453f42bd..9dd6866c 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/JTextField/JTextFieldExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JTextFieldExt { private JTextFieldExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java b/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java index 7409858e..1220dfd7 100644 --- a/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java +++ b/MultiSubDownloader/src/main/java/extensions/javax/swing/text/JTextComponent/JTextComponentExt.java @@ -6,8 +6,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JTextComponentExt { private JTextComponentExt() { diff --git a/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java b/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java index 528b34ad..230152a9 100644 --- a/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java +++ b/MultiSubDownloader/src/main/java/extensions/org/apache/commons/cli/CommandLine/CommandLineExt.java @@ -3,9 +3,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; import org.apache.commons.cli.CommandLine; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.cli.CliOption; @Extension +@NullMarked public class CommandLineExt { private CommandLineExt() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java index 902288a5..8dfc37d9 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/App.java @@ -15,6 +15,8 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.cli.CliOption; import org.lodder.subtools.multisubdownloader.exceptions.CliException; import org.lodder.subtools.multisubdownloader.framework.Bootstrapper; @@ -31,14 +33,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class App { private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static SettingsControl prefCtrl; - private static Splash splash; + private static @Nullable Splash splash; - public static void main(String[] args) throws ReflectiveOperationException, UnsupportedLookAndFeelException { + static void main(String[] args) throws ReflectiveOperationException, UnsupportedLookAndFeelException { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); CommandLineParser parser = new DefaultParser(); @@ -52,9 +55,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu return; } - if (!line.hasCliOption(CliOption.NO_GUI)) { - splash = new Splash().showSplash(); - } + splash = line.hasCliOption(CliOption.NO_GUI) ? null : new Splash().showSplash(); Preferences preferences = Preferences.userRoot(); preferences.putBoolean(CliOption.SPEEDY.value, line.hasCliOption(CliOption.SPEEDY)); @@ -108,7 +109,7 @@ public static void main(String[] args) throws ReflectiveOperationException, Unsu } new Thread(() -> { List providerNames = - app.makeSubtitleProviderStore().getAllProviders().stream().map(provider -> provider.provider) + app.makeSubtitleProviderStore().allProviders.stream().map(provider -> provider.provider) .map(providerName -> providerName.contains("-") ? providerName.split("-")[0] : providerName) .map(providerName -> providerName + "-").toList(); manager.getCache(CacheType.DISK, key -> providerNames.stream().noneMatch(key.provider::equals)) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java index 0823f4ad..da449785 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/CLI.java @@ -2,12 +2,12 @@ import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; import org.apache.commons.cli.CommandLine; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.DownloadAction; import org.lodder.subtools.multisubdownloader.actions.FileListAction; import org.lodder.subtools.multisubdownloader.actions.UserInteractionHandlerAction; @@ -25,11 +25,12 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class CLI { private static final Logger LOGGER = LoggerFactory.getLogger(CLI.class); @@ -79,9 +80,9 @@ public void run() { this.search(); } - public void download(List releases) { + public void download(List releases) { Info.downloadOptions(this.settings, true); - for (Release release : releases) { + for (ReleaseWithPath release : releases) { try { this.download(release); } catch (Exception e) { @@ -112,18 +113,18 @@ public void search() { } } - private void download(Release release) { + private void download(ReleaseWithPath release) { List selection; if (downloadAll) { - selection = release.getMatchingSubs(); + selection = release.matchingSubs; if (!selection.isEmpty()) { - System.out.println("Downloading ALL found subtitles for release: ${release.fileName}"); + System.out.println("Downloading ALL found subtitles for release: ${release.fileNameOrName}"); } } else { selection = userInteractionHandlerAction.subtitleSelection(release, subtitleSelection, dryRun); } if (selection.isEmpty()) { - System.out.println("No subtitles found for: ${release.fileName}"); + System.out.println("No subtitles found for: ${release.fileNameOrName}"); } else { AtomicInteger counter = new AtomicInteger(1); IntStream.range(0, selection.size()).forEach(j -> { @@ -143,7 +144,7 @@ private List getFolders(CommandLine line) { if (line.hasCliOption(CliOption.FOLDER)) { return List.of(Path.of(line.getCliOptionValue(CliOption.FOLDER))); } else { - return new ArrayList<>(this.settings.defaultFolders); + return List.copyOf(this.settings.defaultFolders); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java index 6769e579..615f107d 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/GUI.java @@ -24,6 +24,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.gui.Menu; import org.lodder.subtools.multisubdownloader.gui.actions.search.FileGuiSearchAction; @@ -69,6 +70,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class GUI extends JFrame implements PropertyChangeListener { @Serial private static final long serialVersionUID = 1L; @@ -214,7 +216,6 @@ private void createMenu(LoggingPanel pnlLogging) { .withShowOnlyFound(settings.optionsShowOnlyFound) .withFileQuitAction(this::close) .withViewFilenameAction(() -> visibilityFunction.accept(FILENAME, menuBar.isViewFilenameSelected())) - .withViewTypeAction(() -> visibilityFunction.accept(TYPE, menuBar.isViewTypeSelected())) .withViewTitleAction(() -> visibilityFunction.accept(TITLE, menuBar.isViewTitleSelected())) .withViewSeasonAction(() -> visibilityFunction.accept(SEASON, menuBar.isViewSeasonSelected())) .withViewEpisodeAction(() -> visibilityFunction.accept(EPISODE, menuBar.isViewEpisodeSelected())) @@ -315,7 +316,6 @@ private CustomTable createVideoTable() { customTable.hideColumn(SearchColumnName.OBJECT); customTable.hideColumn(SearchColumnName.SEASON); customTable.hideColumn(SearchColumnName.EPISODE); - customTable.hideColumn(SearchColumnName.TYPE); customTable.hideColumn(SearchColumnName.TITLE); return customTable; } @@ -334,7 +334,6 @@ private void restoreScreenSettings() { menuBar::withViewEpisodeSelected); visibilityConsumer.accept(FILENAME, screenSettings.hideFilename, menuBar::withViewFileNameSelected); visibilityConsumer.accept(SearchColumnName.SEASON, screenSettings.hideSeason, menuBar::withViewSeasonSelected); - visibilityConsumer.accept(SearchColumnName.TYPE, screenSettings.hideType, menuBar::withViewTypeSelected); visibilityConsumer.accept(SearchColumnName.TITLE, screenSettings.hideTitle, menuBar::withViewTitleSelected); } @@ -490,7 +489,6 @@ private void storeScreenSettings() { settingsControl.settings.screenSettings.hideFilename = customTable.isHideColumn(FILENAME); settingsControl.settings.screenSettings.hideSeason = customTable.isHideColumn(SearchColumnName.SEASON); settingsControl.settings.screenSettings.hideTitle = customTable.isHideColumn(SearchColumnName.TITLE); - settingsControl.settings.screenSettings.hideType = customTable.isHideColumn(SearchColumnName.TYPE); } public ProgressDialog setProgressDialog(Cancelable worker) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java index 698083e6..92ed3e0d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UpdateAvailableGithub.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; import org.jsoup.nodes.Element; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.settings.model.UpdateCheckPeriod; @@ -29,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class UpdateAvailableGithub { private static final Logger LOGGER = LoggerFactory.getLogger(UpdateAvailableGithub.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java index 8f6ca21c..17ccd6b4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandler.java @@ -2,9 +2,11 @@ import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public interface UserInteractionHandler extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler { default List getAutomaticSelection(List subtitles) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java index 1613d687..7fb8bc27 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerCLI.java @@ -9,11 +9,13 @@ import java.util.stream.Stream; import extensions.org.codehaus.plexus.components.interactivity.Prompter.PrompterExt; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class UserInteractionHandlerCLI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerCLI implements UserInteractionHandler { @@ -24,10 +26,10 @@ public UserInteractionHandlerCLI(UserInteractionSettingsIntf settings) { @Override public List selectSubtitles(Release release) { System.out.printf("\n%s : %s%n", getText("SelectDialog.SelectCorrectSubtitleThisRelease"), - release.fileName); + release.fileNameOrName); return PrompterExt.promptValuesFromList(prompter, getText("SelectDialog.EnterListSelectedSubtitles"), - release.getMatchingSubs(), + release.matchingSubs, Subtitle::getFileName, true, createTableDisplayer(), @@ -42,7 +44,7 @@ private PrompterExt.ColumnDisplayer createSubtitleDisplayer(SubtitleTa @Override public void dryRunOutput(Release release) { - createTableDisplayer().display(release.getMatchingSubs()); + createTableDisplayer().display(release.matchingSubs); } private PrompterExt.TableDisplayer createTableDisplayer() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java index da3e7cee..ba3e3f1f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/UserInteractionHandlerGUI.java @@ -3,22 +3,25 @@ import javax.swing.*; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.gui.dialog.SelectDialog; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class UserInteractionHandlerGUI extends org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandlerGUI implements UserInteractionHandler { - public UserInteractionHandlerGUI(UserInteractionSettingsIntf settings, JFrame frame) { + public UserInteractionHandlerGUI(UserInteractionSettingsIntf settings, @Nullable JFrame frame) { super(settings, frame); } @Override public List selectSubtitles(Release release) { - List selection = new SelectDialog(frame, release.getMatchingSubs(), release).getSelection(); - return selection.stream().map(release.getMatchingSubs()::get).toList(); + List selection = new SelectDialog(frame, release.matchingSubs, release).getSelection(); + return selection.stream().map(release.matchingSubs::get).toList(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/ActionException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/ActionException.java index 7256701d..3ab6493a 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/ActionException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/ActionException.java @@ -2,6 +2,9 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class ActionException extends Exception { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java index a50d0457..4e39ae63 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/CleanAction.java @@ -6,11 +6,13 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class CleanAction { private static final Logger LOGGER = LoggerFactory.getLogger(CleanAction.class); @@ -23,13 +25,13 @@ public CleanAction(LibrarySettings librarySettings) { this.librarySettings = librarySettings; } - public void cleanUpFiles(Release release, Path destination, String videoFileName) throws IOException { + public void cleanUpFiles(ReleaseWithPath release, Path destination, String videoFileName) throws IOException { LOGGER.trace("cleanUpFiles: LibraryOtherFileAction {}", librarySettings.otherFileAction); if (!destination.isDirectory()) { throw new IllegalArgumentException("Destination [%s] is not a folder".formatted(destination)); } - release.getPath().list() + release.path.list() .filter(p -> (p.isDirectory() && p.fileNameContainsIgnoreCase(SAMPLE_DIR_NAME)) || (p.isRegularFile() && FILE_FILTERS.contains(p.getExtension()))) .forEachEx(p -> { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java index 9e5f9c52..938d9ab4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/DownloadAction.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.actions; +import static util.Utils.*; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -7,7 +9,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import name.falgout.jeffrey.throwing.ThrowingFunction; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; @@ -15,14 +19,17 @@ import org.lodder.subtools.multisubdownloader.lib.library.PathLibraryBuilder; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.multisubdownloader.settings.model.Settings; -import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +import org.lodder.subtools.sublibrary.util.Nothing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class DownloadAction { private static final Logger LOGGER = LoggerFactory.getLogger(DownloadAction.class); @@ -37,19 +44,20 @@ public DownloadAction(Settings settings, Manager manager, UserInteractionHandler this.userInteractionHandler = userInteractionHandler; } - public void download(Release release, Subtitle subtitle, @Nullable AtomicInteger counter=null) throws IOException { + public void download(ReleaseWithPath release, Subtitle subtitle, + @Nullable AtomicInteger counter=null) throws IOException { LOGGER.info("Downloading subtitle: [{}] for release: [{}]", subtitle.fileName, release.fileName); - switch (release.videoType) { - case EPISODE -> download(release, subtitle, settings.episodeLibrarySettings, counter); - case MOVIE -> download(release, subtitle, settings.movieLibrarySettings, counter); - default -> throw new IllegalArgumentException("Unexpected value: " + release.videoType); + switch (release) { + case TvReleaseWithPath _ -> download(release, subtitle, settings.episodeLibrarySettings, counter); + case MovieReleaseWithPath _ -> download(release, subtitle, settings.movieLibrarySettings, counter); } } - private void download(Release release, Subtitle subtitle, LibrarySettings librarySettings, + private void download(ReleaseWithPath release, Subtitle subtitle, LibrarySettings librarySettings, @Nullable AtomicInteger counter) throws IOException { LOGGER.trace("cleanUpFiles: LibraryAction {}", librarySettings.action); - Path path = PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); + Path path = + PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).buildPath(release); if (!path.exists()) { LOGGER.debug("Download creating folder [{}] ", path.toAbsolutePath()); try { @@ -61,13 +69,12 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar FilenameLibraryBuilder filenameLibraryBuilder = FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); - String videoFileName = filenameLibraryBuilder.build(release).toString(); + String videoFileName = filenameLibraryBuilder.buildPath(release).toString(); - Function fileNameFunction = counterOverride -> + ThrowingFunction incrementCounter = AtomicInteger::incrementAndGet; + Function<@Nullable AtomicInteger, String> fileNameFunction = counterOverride -> filenameLibraryBuilder.buildSubtitle(release, subtitle, videoFileName, - counter == null ? - (counterOverride == null ? null : counterOverride.incrementAndGet()) : - Integer.valueOf(counter.incrementAndGet())); + ifNotNullOrElseGet(counter, incrementCounter, () -> ifNotNull(counterOverride, incrementCounter))); List downloadedSubtitles; try { @@ -82,7 +89,7 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar } if (!librarySettings.hasLibraryAction(LibraryActionType.NOTHING)) { - Path oldLocationFile = release.getPath().resolve(release.fileName); + Path oldLocationFile = release.path.resolve(ifNullThen(release.fileName, release.name)); if (oldLocationFile.exists()) { LOGGER.info("Moving/Renaming [{}] to folder [{}] this might take a while... ", videoFileName, path); oldLocationFile.moveToDir(path); @@ -96,8 +103,7 @@ private void download(Release release, Subtitle subtitle, LibrarySettings librar } } if (librarySettings.backupSubtitle) { - String langFolder = subtitle.language == null ? Language.ENGLISH.iso639_3 : subtitle.language.iso639_3; - Path backupPath = librarySettings.backupSubtitlePath.resolve(langFolder); + Path backupPath = librarySettings.backupSubtitlePath.resolve(subtitle.language.iso639_3); if (!backupPath.exists()) { try { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java index 91c786b6..0d6b2513 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/FileListAction.java @@ -9,6 +9,8 @@ import extensions.java.nio.file.Path.PathExt; import manifold.ext.props.rt.api.set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.listeners.IndexingProgressListener; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Language; @@ -16,13 +18,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class FileListAction { private static final Logger LOGGER = LoggerFactory.getLogger(FileListAction.class); private static final String SUBTITLE_EXTENSION = "srt"; private final Settings settings; - @set IndexingProgressListener indexingProgressListener; + @set @Nullable IndexingProgressListener indexingProgressListener; public FileListAction(Settings settings) { this.settings = settings; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/MoveAndRenameAction.java similarity index 77% rename from MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java rename to MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/MoveAndRenameAction.java index c4414140..dd6eaaff 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/RenameAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/MoveAndRenameAction.java @@ -4,6 +4,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.library.FilenameLibraryBuilder; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; @@ -13,28 +14,30 @@ import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RenameAction { +@NullMarked +public class MoveAndRenameAction { - private static final Logger LOGGER = LoggerFactory.getLogger(RenameAction.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MoveAndRenameAction.class); private final LibrarySettings librarySettings; private final Manager manager; private final UserInteractionHandler userInteractionHandler; - public RenameAction(LibrarySettings librarySettings, Manager manager, + public MoveAndRenameAction(LibrarySettings librarySettings, Manager manager, UserInteractionHandler userInteractionHandler) { this.librarySettings = librarySettings; this.manager = manager; this.userInteractionHandler = userInteractionHandler; } - public void rename(Path f, Release release) { + public void moveAndRename(Path f, ReleaseWithPath release) { String filename = switch (librarySettings.action) { - case MOVE, NOTHING -> f.getFileNameAsString(); + case MOVE, NOTHING -> f.fileNameAsString; case MOVE_AND_RENAME, RENAME -> getNewFilename(f, release); }; LOGGER.trace("rename: filename [{}]", filename); @@ -42,8 +45,8 @@ public void rename(Path f, Release release) { Path newDir = switch (librarySettings.action) { case MOVE, MOVE_AND_RENAME -> - PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).build(release); - case RENAME, NOTHING -> release.getPath(); + PathLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler).buildPath(release); + case RENAME, NOTHING -> release.path; }; if (!newDir.exists()) { LOGGER.debug("Creating dir [{}]", newDir.toAbsolutePath()); @@ -56,7 +59,7 @@ public void rename(Path f, Release release) { } LOGGER.trace("rename: newDir [{}]", newDir); - Path file = release.getPath().resolve(release.fileName); + Path file = release.path.resolve(release.fileName); try { if (librarySettings.hasLibraryAction(LibraryActionType.MOVE) || @@ -65,15 +68,15 @@ public void rename(Path f, Release release) { file.moveToDirAndRename(newDir, filename); } else { LOGGER.info("Moving [{}] to the library folder [{}] , this might take a while... ", filename, - release.getPath()); - file.moveToDirAndRename(release.getPath(), filename); + release.path); + file.moveToDirAndRename(release.path, filename); } if (!librarySettings.hasLibraryOtherFileAction(LibraryOtherFileActionType.NOTHING)) { new CleanAction(librarySettings).cleanUpFiles(release, newDir, filename); } - if (librarySettings.removeEmptyFolders && release.getPath().isEmptyDir()) { - Files.delete(release.getPath()); + if (librarySettings.removeEmptyFolders && release.path.isEmptyDir()) { + Files.delete(release.path); } } catch (IOException e) { LOGGER.error("Unsuccessful in moving the file to the library", e); @@ -83,8 +86,8 @@ public void rename(Path f, Release release) { private String getNewFilename(Path f, Release release) { FilenameLibraryBuilder filenameLibraryBuilder = FilenameLibraryBuilder.fromSettings(librarySettings, manager, userInteractionHandler); - String filename = filenameLibraryBuilder.build(release).toString(); - if (release.hasExtension("srt")) { + String filename = filenameLibraryBuilder.buildPathStructure(release); + if (release.fileNameOrName.endsWith(".srt")) { Language language = null; if (librarySettings.includeLanguageCode) { language = DetectLanguage.execute(f); @@ -92,7 +95,7 @@ private String getNewFilename(Path f, Release release) { LOGGER.error("Unable to detect language, leaving language code blank"); } } - return filenameLibraryBuilder.buildSubtitle(release, filename, language, 0); + return filenameLibraryBuilder.buildSubtitle(release.fileNameOrName, filename, language, 0); } return filename; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java index 38ff23bb..2d5c91cb 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/SearchAction.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.actions; +import static java.util.Objects.*; import static manifold.ext.props.rt.api.PropOption.*; import java.util.List; @@ -7,6 +8,8 @@ import manifold.ext.props.rt.api.get; import manifold.ext.props.rt.api.set; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException; @@ -23,16 +26,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class SearchAction implements Runnable, Cancelable, SearchHandler { +@NullMarked +public abstract class SearchAction implements Runnable, Cancelable, SearchHandler { private static final Logger LOGGER = LoggerFactory.getLogger(SearchAction.class); @val(Protected) Settings settings; @val(Protected) SubtitleProviderStore subtitleProviderStore; - @get(Protected) @set(Private) StatusListener statusListener; - @get(Protected) @set(Private) SearchManager searchManager; - @get(Protected) @set(Private) List releases; + @get(Protected) @set(Private) @Nullable StatusListener statusListener; + @get(Protected) @set(Private) @Nullable SearchManager searchManager; + @get(Protected) @set(Private) @Nullable List releases; @get(Protected) abstract Language language; abstract @get(Protected) IndexingProgressListener indexingProgressListener; abstract @get(Protected) UserInteractionHandler userInteractionHandler; @@ -94,7 +98,7 @@ private void search() throws ActionException { /* Tell the manager which providers to use */ searchManager.reset(); - this.subtitleProviderStore.getAllProviders().stream() + this.subtitleProviderStore.allProviders.stream() .filter(subtitleProvider -> settings.useSerieSource(subtitleProvider.source)) .forEach(searchManager::addProvider); @@ -109,10 +113,10 @@ private void search() throws ActionException { protected abstract void validate() throws SearchSetupException; - protected abstract List createReleases(); + protected abstract List createReleases(); protected void setStatusMessage(String message) { - this.statusListener.onStatus(message); + requireNonNull(this.statusListener).onStatus(message); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java index 24bf9655..d4444958 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/actions/UserInteractionHandlerAction.java @@ -2,6 +2,7 @@ import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.SubtitleComparator; import org.lodder.subtools.multisubdownloader.settings.model.Settings; @@ -11,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class UserInteractionHandlerAction { private static final Logger LOGGER = LoggerFactory.getLogger(UserInteractionHandlerAction.class); @@ -28,7 +30,7 @@ public UserInteractionHandlerAction(Settings settings, UserInteractionHandler us * @param subtitleSelectionDialog subtitleSelectionDialog * @return integer which subtitle is selected for downloading */ - public List subtitleSelection(final Release release, final boolean subtitleSelectionDialog) { + public List subtitleSelection(Release release, boolean subtitleSelectionDialog) { return this.subtitleSelection(release, subtitleSelectionDialog, false); } @@ -42,7 +44,7 @@ public List subtitleSelection(Release release, final boolean subtitleS final boolean dryRun) { // Sort subtitles by score - List subs = release.getMatchingSubs().stream().sorted(new SubtitleComparator()).toList(); + List subs = release.matchingSubs.stream().sorted(new SubtitleComparator()).toList(); if (dryRun) { if (!subs.isEmpty()) { userInteractionHandler.dryRunOutput(release); @@ -50,7 +52,7 @@ public List subtitleSelection(Release release, final boolean subtitleS } else { if (!subs.isEmpty()) { LOGGER.debug("determineWhatSubtitleDownload for videoFile: [{}] # found subs: [{}]", - release.fileName, subs.size()); + release.fileNameOrName, subs.size()); if (settings.optionsAlwaysConfirm) { return userInteractionHandler.selectSubtitles(release); } else if (subs.size() == 1 && subs.first.subtitleMatchType == SubtitleMatchType.EXACT) { @@ -76,14 +78,14 @@ public List subtitleSelection(Release release, final boolean subtitleS return userInteractionHandler.selectSubtitles(release); } else { LOGGER.info("Multiple subs detected for: [{}] Unhandleable for CMD! switch to GUI or use " + - "'--selection' as switch in de CMD", release.fileName); + "'--selection' as switch in de CMD", release.fileNameOrName); } } else { LOGGER.debug("determineWhatSubtitleDownload: only one sub taking it!!!!"); return List.of(subs.first); } } - LOGGER.debug("determineWhatSubtitleDownload: No subs found for [{}]", release.fileName); + LOGGER.debug("determineWhatSubtitleDownload: No subs found for [{}]", release.fileNameOrName); } return List.of(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java index 84f0a732..e0e47c78 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/CliOption.java @@ -1,8 +1,11 @@ package org.lodder.subtools.multisubdownloader.cli; -import manifold.ext.props.rt.api.get; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum CliOption { HELP("help", false, "App.OptionHelpMsg"), NO_GUI("nogui", false, "App.OptionNoGuiMsg"), @@ -20,24 +23,19 @@ public enum CliOption { DRY_RUN("dryrun", false, "App.OptionDryRun"), CONFIRM_PROVIDER_MAPPING("confirmProviderMapping", false, "App.OptionConfirmProviderMapping"); - @get String value; - @get String longValue; - @get boolean hasArg; - @get String msgCode; + @val String value; + @val @Nullable String longValue; + @val boolean hasArg; + @val String msgCode; - CliOption(String value, String longValue, boolean hasArg, String msgCode) { + CliOption(String value, @Nullable String longValue=null, boolean hasArg, String msgCode) { this.value = value; this.longValue = longValue; this.hasArg = hasArg; this.msgCode = msgCode; } - CliOption(String value, boolean hasArg, String description) { - this(value, null, hasArg, description); - } - public String getDescription() { return Messages.getText(msgCode); } - } \ No newline at end of file diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java index a416d34a..74e68945 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/actions/CliSearchAction.java @@ -8,6 +8,7 @@ import manifold.ext.props.rt.api.get; import manifold.ext.props.rt.api.override; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.CLI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; @@ -22,12 +23,13 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CliSearchAction extends SearchAction { +@NullMarked +public class CliSearchAction extends SearchAction { private static final Logger LOGGER = LoggerFactory.getLogger(CliSearchAction.class); @@ -65,7 +67,7 @@ public CliSearchAction(Settings settings, SubtitleProviderStore subtitleProvider } @Override - protected List createReleases() { + protected List createReleases() { fileListAction.indexingProgressListener = this.indexingProgressListener; List files = this.folders.stream() @@ -85,7 +87,7 @@ protected List createReleases() { System.out.println(Messages.getText("CliSearchAction.ParsingFoundFiles")); this.indexingProgressListener.progress(progress); - List releases = new ArrayList<>(); + List releases = new ArrayList<>(); for (Path file : files) { index++; progress = (int) Math.floor((float) index / total * 100); @@ -103,7 +105,7 @@ protected List createReleases() { } @Override - public void onFound(Release release, List subtitles) { + public void onFound(ReleaseWithPath release, List subtitles) { subtitles.stream() .filter(subtitle -> filtering.useSubtitle(subtitle, release)) .forEach(release::addMatchingSub); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java index 304a9806..9d3228b6 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIFileIndexerProgress.java @@ -1,8 +1,10 @@ package org.lodder.subtools.multisubdownloader.cli.progress; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.listeners.IndexingProgressListener; +@NullMarked public final class CLIFileIndexerProgress extends CLIProgress implements IndexingProgressListener { private String currentFile; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java index 50a0f2f8..8bc3fc1e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLIProgress.java @@ -4,7 +4,9 @@ import manifold.ext.props.rt.api.var; import manifold.ext.rt.api.Self; +import org.jspecify.annotations.NullMarked; +@NullMarked abstract sealed class CLIProgress permits CLIFileIndexerProgress, CLISearchProgress { @var(Protected) int progress; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java index a4a0ef74..6cab3b7b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/cli/progress/CLISearchProgress.java @@ -1,12 +1,14 @@ package org.lodder.subtools.multisubdownloader.cli.progress; import dnl.utils.text.table.TextTable; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.gui.dialog.progress.search.SearchProgressTableModel; import org.lodder.subtools.multisubdownloader.listeners.SearchProgressListener; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public final class CLISearchProgress extends CLIProgress implements SearchProgressListener { private final TextTable table; @@ -19,7 +21,13 @@ public CLISearchProgress() { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { - this.tableModel.update(provider.provider, jobsLeft, release == null ? "Done" : release.fileName); + this.tableModel.update(provider.provider, jobsLeft, release.fileNameOrName); + this.printProgress(); + } + + @Override + public void done(SubtitleProvider provider) { + this.tableModel.update(provider.provider, 0, "Done"); this.printProgress(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/CliException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/CliException.java index a089bb23..e4648a8e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/CliException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/CliException.java @@ -2,6 +2,9 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class CliException extends Exception { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java index 416e4b59..fa2b203b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/exceptions/SearchSetupException.java @@ -2,8 +2,10 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.ActionException; +@NullMarked public class SearchSetupException extends ActionException { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java index 79e60aa6..0c009526 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Bootstrapper.java @@ -6,6 +6,7 @@ import java.util.Set; import java.util.prefs.Preferences; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProviderComparator; @@ -16,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class Bootstrapper { private static final Logger LOGGER = LoggerFactory.getLogger(Bootstrapper.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java index 330c217c..f64f7d30 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/Container.java @@ -4,12 +4,14 @@ import java.util.Map; import java.util.prefs.Preferences; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; +@NullMarked public class Container { private final Map> bindings = new HashMap<>(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java index 5e1cf9b3..d44c9fd5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Emitter.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class Emitter { private final Map> eventListeners = new HashMap<>(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java index 14c1c77d..77331b58 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Event.java @@ -3,7 +3,9 @@ import java.util.Collection; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public class Event { @val String eventName; private final EventBag eventBag; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/EventBag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/EventBag.java index c2e0a919..e3335d58 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/EventBag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/EventBag.java @@ -4,6 +4,9 @@ import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class EventBag { protected final Map attributes = new HashMap<>(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Handler.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Handler.java index 6b66e37f..653f65ea 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Handler.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/event/Handler.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.framework.event; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface Handler { void handle(Event event); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java index 77445b9d..d152a09b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/EventServiceProvider.java @@ -2,11 +2,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; +@NullMarked public class EventServiceProvider implements ServiceProvider { @val @override int priority = 0; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java index 87c6a642..0d6fcad1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProvider.java @@ -1,9 +1,11 @@ package org.lodder.subtools.multisubdownloader.framework.service.providers; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; +@NullMarked public interface ServiceProvider { @val int priority; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java index 1a8adb03..4f337441 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/framework/service/providers/ServiceProviderComparator.java @@ -4,6 +4,9 @@ import java.io.Serializable; import java.util.Comparator; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class ServiceProviderComparator implements Comparator, Serializable { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java index 8633f448..5df01bcb 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Menu.java @@ -6,6 +6,9 @@ import java.awt.event.ActionListener; import java.io.Serial; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class Menu extends JMenuBar { @Serial @@ -254,9 +257,6 @@ private void addActionListener(JMenuItem menuItem, Runnable actionListener) { } private void addActionListener(JMenuItem menuItem, ActionListener actionListener) { - if (actionListener != null) { - menuItem.addActionListener(actionListener); - } + menuItem.addActionListener(actionListener); } - } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java index 705c5a65..691ae350 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/Splash.java @@ -7,7 +7,9 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; +@NullMarked public class Splash extends JWindow { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java index 561eed4f..30a0a28e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/ToStringListCellRenderer.java @@ -6,6 +6,9 @@ import java.awt.*; import java.util.function.Function; +import org.jspecify.annotations.NullMarked; + +@NullMarked public final class ToStringListCellRenderer implements ListCellRenderer { private final ListCellRenderer originalRenderer; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java index 4ef8bb3c..e4a7e313 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/FileGuiSearchAction.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.actions.FileListAction; @@ -16,12 +16,13 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.Language; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; -public final class FileGuiSearchAction extends GuiSearchAction { +@NullMarked +public final class FileGuiSearchAction extends GuiSearchAction { - private final @NonNull FileListAction filelistAction; + private final FileListAction filelistAction; public FileGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { @@ -38,11 +39,11 @@ protected void validate() throws SearchSetupException { } @Override - public void onFound(Release release, List subtitles) { + public void onFound(ReleaseWithPath release, List subtitles) { VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); - List filteredSubtitles = filtering != null ? - subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList() : subtitles; + List filteredSubtitles = + subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList(); filteredSubtitles.forEach(release::addMatchingSub); model.addRow(release); @@ -53,7 +54,7 @@ public void onFound(Release release, List subtitles) { } @Override - protected List createReleases() { + protected List createReleases() { SearchFileInputPanel inputPanel = getInputPanel(); String filePath = inputPanel.getIncomingPath(); Language language = inputPanel.getSelectedLanguage(); @@ -70,9 +71,9 @@ protected List createReleases() { return createReleases(files); } - private List createReleases(List files) { + private List createReleases(List files) { /* parse every video file */ - List releases = new ArrayList<>(); + List releases = new ArrayList<>(); int total = files.size(); int index = 0; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java index bb2183ed..dc3fe449 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/GuiSearchAction.java @@ -6,6 +6,7 @@ import manifold.ext.props.rt.api.get; import manifold.ext.props.rt.api.override; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; import org.lodder.subtools.multisubdownloader.actions.SearchAction; @@ -22,7 +23,8 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -public abstract sealed class GuiSearchAction

extends SearchAction +@NullMarked +public abstract sealed class GuiSearchAction

extends SearchAction permits FileGuiSearchAction, TextGuiSearchAction { @get(Protected) GUI mainWindow; @@ -61,7 +63,7 @@ protected Language getLanguage() { } @Override - public void onFound(Release release, List subtitles) { + public void onFound(R release, List subtitles) { if (Thread.currentThread().isInterrupted()) { return; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java index 7830562e..3ad83c74 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/actions/search/TextGuiSearchAction.java @@ -1,8 +1,8 @@ package org.lodder.subtools.multisubdownloader.gui.actions.search; -import java.nio.file.Path; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.exceptions.SearchSetupException; @@ -12,14 +12,14 @@ import org.lodder.subtools.multisubdownloader.lib.ReleaseFactory; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; -import org.lodder.subtools.sublibrary.control.VideoPatterns.VideoExtensions; -import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.VideoSearchType; -public final class TextGuiSearchAction extends GuiSearchAction { +@NullMarked +public final class TextGuiSearchAction extends GuiSearchAction { public TextGuiSearchAction(Settings settings, SubtitleProviderStore subtitleProviderStore, GUI mainWindow, SearchPanel searchPanel, ReleaseFactory releaseFactory) { @@ -44,11 +44,12 @@ protected List createReleases() { // TODO: Redefine what a "release" is. return switch (type) { case EPISODE -> List.of( - new TvRelease(name:name, season:inputPanel.season, episode:inputPanel.episode, quality:inputPanel.quality)); - case MOVIE -> List.of(new MovieRelease(name:name, quality:inputPanel.quality)); - default -> releaseFactory.createRelease(Path.of( - name + (VideoExtensions.values().stream().anyMatch(ext -> name.endsWith("." + ext)) ? "" : ".")), - userInteractionHandler).map(List::of).orElseGet(List::of); + new TvReleaseWithoutPath(name:name, season:inputPanel.season, episode:inputPanel.episode, quality: + inputPanel.quality, completeName:name)); + case MOVIE -> + List.of(new MovieReleaseWithoutPath(name:name, quality:inputPanel.quality, completeName:name)); + default -> releaseFactory.createRelease(name, userInteractionHandler).map(Release.class::cast).map(List::of) + .orElseGet(List::of); }; } @@ -56,8 +57,8 @@ protected List createReleases() { public void onFound(Release release, List subtitles) { VideoTableModel model = (VideoTableModel) this.searchPanel.resultPanel.getTable().getModel(); - List subtitlesFiltered = filtering != null ? - subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList() : subtitles; + List subtitlesFiltered = + subtitles.stream().filter(subtitle -> filtering.useSubtitle(subtitle, release)).toList(); subtitlesFiltered.forEach(release::addMatchingSub); // use automatic selection to reduce the selection for the user diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/Cancelable.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/Cancelable.java index 0872bb69..882443e5 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/Cancelable.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/Cancelable.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.gui.dialog; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface Cancelable { boolean cancel(boolean mayInterruptIfRunning); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java index 1ef26777..16d193b2 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MappingEpisodeNameDialog.java @@ -11,7 +11,6 @@ import java.io.Serial; import java.util.Comparator; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Vector; @@ -20,6 +19,7 @@ import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.function.TriFunction; import org.apache.commons.lang3.tuple.Pair; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; @@ -30,9 +30,10 @@ import org.lodder.subtools.sublibrary.cache.ProviderCacheKey; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; +@NullMarked public class MappingEpisodeNameDialog extends MultiSubDialog { @Serial private static final long serialVersionUID = 1L; @@ -99,12 +100,13 @@ public MappingEpisodeNameDialog(@Nullable JFrame frame=null, Manager manager, currentName); selectedSubtitleProvider.ifPresent(provider -> userInteractionHandler.enter(message).ifPresent(newName -> { - TvRelease tvRelease = new TvRelease( + TvReleaseWithoutPath tvRelease = new TvReleaseWithoutPath( name:currentName, season:row.serieMapping.season, episode:1, originalName:currentName, - customName:newName); + customName:newName, + completeName:currentName); try { provider.getProviderSerieMapping(tvRelease).ifPresentOrElse(serieId -> { row.serieMapping = @@ -131,8 +133,7 @@ public MappingEpisodeNameDialog(@Nullable JFrame frame=null, Manager manager, private void selectMappingType(MappingType mappingType) { this.selectedMappingType = mappingType; - this.selectedSubtitleProvider = subtitleProviderStore.getAllProviders() - .stream() + this.selectedSubtitleProvider = subtitleProviderStore.allProviders.stream() .filter(subtitleProvider -> subtitleProvider.source.name.equals(mappingType.provider)) .findAny(); btnAddCustomMapping.enabled = selectedSubtitleProvider.isPresent(); @@ -140,6 +141,7 @@ private void selectMappingType(MappingType mappingType) { repaint(); } + @NullMarked public enum MappingType { TVDB("TVDB", "TVDB", "EPISODEmapping"), IMDB("IMDB", "IMDB", "EPISODEmapping"), @@ -185,12 +187,13 @@ public List> getValues(Manager manager) { } } + @NullMarked private static class Row extends Vector { @Serial private static final long serialVersionUID = 1L; @val ProviderCacheKey key; - @var SerieMapping serieMapping; + @var @Nullable SerieMapping serieMapping; - public Row(ProviderCacheKey key, String name, String providerId, String providerName, + public Row(ProviderCacheKey key, String name, String providerId, @Nullable String providerName, SerieMapping serieMapping) { this.key = key; this.serieMapping = serieMapping; @@ -200,6 +203,7 @@ public Row(ProviderCacheKey key, String name, String providerId, String provider } } + @NullMarked private static class MappingTableModel extends DefaultTableModel { @Serial private static final long serialVersionUID = 1L; @val Manager manager; @@ -211,9 +215,8 @@ public MappingTableModel(Manager manager) { void setMappingType(MappingType mappingType) { setDataVector(null, new String[]{mappingType.nameColumn, mappingType.mappingColumn, mappingType.providerNameColumn}); - mappingType.getValues(manager) - .stream() - .map(serieMappingPair -> { + mappingType.getValues(manager).stream() + .mapFilterNonNull(serieMappingPair -> { SerieMapping serieMapping = serieMappingPair.getValue(); if (serieMapping == null) { return null; @@ -225,10 +228,8 @@ void setMappingType(MappingType mappingType) { providerId = providerId.replace(".html", ""); return new Row(serieMappingPair.getKey(), serieMapping.name, providerId, serieMapping.providerName, serieMapping); - }).filter(Objects::nonNull) - .sorted(Comparator.comparing( - row -> row.serieMapping == null || row.serieMapping.providerName == null ? "zzz" : - row.serieMapping.name)) + }) + .sorted(Comparator.comparing(row -> row.serieMapping.name)) .forEach(this::addRow); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java index f1c8177f..f3a3f2c4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/MultiSubDialog.java @@ -4,8 +4,10 @@ import java.awt.*; import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +@NullMarked public class MultiSubDialog extends JDialog { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java index dce77c40..a5d3fb61 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/PreferenceDialog.java @@ -3,11 +3,12 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.border.*; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.io.Serial; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.framework.event.Emitter; import org.lodder.subtools.multisubdownloader.framework.event.Event; @@ -21,6 +22,7 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class PreferenceDialog extends MultiSubDialog { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java index 31891f72..5251e9f0 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/ProgressDialog.java @@ -8,10 +8,12 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.gui.extra.progress.Messenger; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; +@NullMarked public class ProgressDialog extends MultiSubDialog implements Messenger { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java index f736e1ce..1fa65d22 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/RenameDialog.java @@ -17,8 +17,9 @@ import com.google.common.collect.Streams; import manifold.ext.props.rt.api.set; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -import org.lodder.subtools.multisubdownloader.actions.RenameAction; +import org.lodder.subtools.multisubdownloader.actions.MoveAndRenameAction; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; @@ -35,6 +36,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class RenameDialog extends MultiSubDialog implements PropertyChangeListener { @Serial private static final long serialVersionUID = 1L; @@ -58,7 +60,7 @@ public RenameDialog(@Nullable JFrame frame=null, Settings settings, VideoType vi fillContents:true) .addToPanel(contentPane, "span, grow, wrap") .addComponent("shrink", new JLabel(getText("PreferenceDialog.Location"))) - .addComponent("grow", this.txtFolder = MyTextFieldPath.builder().requireValue().build().columns(20)) + .addComponent("grow", this.txtFolder = new MyTextFieldPath(true).columns(20)) .addComponent("shrink, wrap", new JButton(getText("App.Browse")).actionListener( () -> MemoryFolderChooser.getInstance() .selectDirectory(contentPane, @@ -122,6 +124,7 @@ public void propertyChange(PropertyChangeEvent event) { } } + @NullMarked private static class TypedRenameWorker extends SwingWorker implements Cancelable { private final UserInteractionHandler userInteractionHandler; @@ -129,7 +132,7 @@ private static class TypedRenameWorker extends SwingWorker impleme private final VideoType videoType; private final Set extensions; private final boolean isRecursive; - private final RenameAction renameAction; + private final MoveAndRenameAction moveAndRenameAction; @set ReleaseFactory releaseFactory; public TypedRenameWorker(Path dir, LibrarySettings librarySettings, VideoType videoType, boolean isRecursive, @@ -140,7 +143,7 @@ public TypedRenameWorker(Path dir, LibrarySettings librarySettings, VideoType vi this.dir = dir; this.videoType = videoType; this.isRecursive = isRecursive; - this.renameAction = new RenameAction(librarySettings, manager, userInteractionHandler); + this.moveAndRenameAction = new MoveAndRenameAction(librarySettings, manager, userInteractionHandler); } @Override @@ -154,9 +157,9 @@ private void rename(Path dir) throws IOException { if (file.isRegularFile()) { if (!file.fileNameContainsIgnoreCase("sample") && extensions.contains(file.getExtension())) { releaseFactory.createRelease(file, userInteractionHandler).ifPresent(release -> { - publish(release.fileName); - if (release.videoType == videoType) { - renameAction.rename(file, release); + publish(release.fileNameOrName); + if (release.isOfType(videoType)) { + moveAndRenameAction.moveAndRename(file, release); } }); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java index bb35b53b..75d8db54 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/SelectDialog.java @@ -13,6 +13,7 @@ import java.util.stream.IntStream; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; import org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName; @@ -20,6 +21,7 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SelectDialog extends MultiSubDialog { @Serial private static final long serialVersionUID = 1L; @@ -32,14 +34,14 @@ public class SelectDialog extends MultiSubDialog { /** * Create the dialog. */ - public SelectDialog(@Nullable JFrame frame = null, List subtitles, Release release) { + public SelectDialog(@Nullable JFrame frame=null, List subtitles, Release release) { super(frame, getText("SelectDialog.SelectCorrectSubtitle"), true); this.subtitles = subtitles.stream().distinct().sorted(Comparator.comparing(Subtitle::getScore).reversed()).toList(); contentPane .layout(new MigLayout("", "[1000px:n,grow,fill]", "[][::100px,fill][grow]")) .addComponent("cell 0 0", - new JLabel(getText("SelectDialog.SelectCorrectSubtitleThisRelease") + release.fileName)) + new JLabel(getText("SelectDialog.SelectCorrectSubtitleThisRelease") + release.fileNameOrName)) .addComponent("cell 0 1,grow", new JScrollPane().viewportView(customTable = createCustomTable())) .addComponent("cell 0 2,grow", new JPanel() .layout(new FlowLayout(FlowLayout.RIGHT)) @@ -52,7 +54,7 @@ public SelectDialog(@Nullable JFrame frame = null, List subtitles, Rel .actionCommand(getText("App.OK"))) .addComponent(new JButton(getText("SelectDialog.Everything")) .actionListener(() -> { - selectedSubtitleIdxs = IntStream.range(0, release.getMatchingSubs().size()).boxed().toList(); + selectedSubtitleIdxs = IntStream.range(0, release.matchingSubCount).boxed().toList(); setVisible(false); }) .actionCommand(getText("App.All"))) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java index a9f0645c..88547812 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/StructureBuilderDialog.java @@ -10,10 +10,10 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.Serial; -import java.nio.file.Path; import java.util.function.Function; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.lib.ReleaseFactory; import org.lodder.subtools.multisubdownloader.lib.library.LibraryBuilder; @@ -23,45 +23,43 @@ import org.lodder.subtools.multisubdownloader.settings.model.structure.SerieStructureTag; import org.lodder.subtools.multisubdownloader.settings.model.structure.StructureTag; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.model.MovieRelease; -import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class StructureBuilderDialog extends MultiSubDialog implements DocumentListener { @Serial private static final long serialVersionUID = 1L; - private final VideoType videoType; - private final StructureType structureType; - private final UserInteractionHandler userInteractionHandler; private final Function libraryBuilder; - private JTextField txtStructure; - private JLabel lblPreview; - private TvRelease tvRelease; - private MovieRelease movieRelease; + private final JTextField txtStructure; + private final JLabel lblPreview; + private final ReleaseWithoutPath release; + private final JPanel tagPanel; private String oldStructure; - private JPanel tagPanel; + @NullMarked public enum StructureType { - FILE, FOLDER + FILE, + FOLDER } public StructureBuilderDialog(@Nullable JFrame frame=null, String title, boolean modal, VideoType videoType, StructureType structureType, Manager manager, UserInteractionHandler userInteractionHandler, Function filenameLibraryBuilder) { super(frame, title, modal); - this.videoType = videoType; - this.structureType = structureType; - this.userInteractionHandler = userInteractionHandler; this.libraryBuilder = filenameLibraryBuilder; - initializeUI(); - generateVideoFiles(manager); - } + this.release = switch (videoType) { + case EPISODE -> new ReleaseFactory(new Settings(), manager).createRelease( + "Terra.Nova.S01E01E02.Genesis.720p.HDTV.x264-ORENJI.mkv", userInteractionHandler, false).orElseThrow(); + case MOVIE -> new ReleaseFactory(new Settings(), manager).createRelease( + "Final.Destination.5.2011.720p.Bluray.x264-TWiZTED.mkv", userInteractionHandler, false).orElseThrow(); + }; + + // Initialize GUI - private void initializeUI() { setBounds(100, 100, 600, 300); setMinimumSize(new Dimension(600, 300)); @@ -102,18 +100,6 @@ private void initializeUI() { } } - private void generateVideoFiles(Manager manager) { - ReleaseFactory releaseFactory = new ReleaseFactory(new Settings(), manager); - switch (videoType) { - case EPISODE -> tvRelease = (TvRelease) releaseFactory.createRelease( - Path.of("Terra.Nova.S01E01E02.Genesis.720p.HDTV.x264-ORENJI.mkv"), - userInteractionHandler, false).orElse(null); - case MOVIE -> movieRelease = (MovieRelease) releaseFactory.createRelease( - Path.of("Final.Destination.5.2011.720p.Bluray.x264-TWiZTED.mkv"), - userInteractionHandler, false).orElse(null); - } - } - private void buildLabelTable(StructureTag[] structureTags) { structureTags.forEach(this::addTag); } @@ -134,14 +120,7 @@ public String showDialog(String structure) { } protected void parseText() { - lblPreview.setText(libraryBuilder.apply(txtStructure.getText()).build(getGeneratedRelease()).toString()); - } - - private Release getGeneratedRelease() { - return switch (videoType) { - case EPISODE -> tvRelease; - case MOVIE -> movieRelease; - }; + lblPreview.setText(libraryBuilder.apply(txtStructure.getText()).buildPathStructure(release)); } @Override @@ -161,6 +140,7 @@ public void removeUpdate(DocumentEvent arg0) { parseText(); } + @NullMarked private class InsertTag implements MouseListener { @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/fileindexer/IndexingProgressDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/fileindexer/IndexingProgressDialog.java index 42826232..d8de52ea 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/fileindexer/IndexingProgressDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/fileindexer/IndexingProgressDialog.java @@ -2,6 +2,7 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; @@ -9,6 +10,7 @@ import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; import org.lodder.subtools.multisubdownloader.listeners.IndexingProgressListener; +@NullMarked public class IndexingProgressDialog extends ProgressDialog implements IndexingProgressListener { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java index 47f0d58d..00970b14 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressDialog.java @@ -9,6 +9,7 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.actions.ActionException; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; @@ -17,6 +18,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public class SearchProgressDialog extends MultiSubDialog implements SearchProgressListener { @Serial @@ -61,10 +63,16 @@ public void windowClosing(WindowEvent e) { @Override public void progress(SubtitleProvider provider, int jobsLeft, Release release) { this.setVisible(); - this.tableModel.update(provider.subtitleProviderFrontEnd.name, jobsLeft, release == null ? "Done" : - release.fileName); + this.tableModel.update(provider.subtitleProviderFrontEnd.name, jobsLeft, release.fileNameOrName); } + @Override + public void done(SubtitleProvider provider) { + this.setVisible(); + this.tableModel.update(provider.subtitleProviderFrontEnd.name, 0, "Done"); + } + + @Override public void progress(int progress) { this.setVisible(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java index d567dfe1..b8bdf75e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/dialog/progress/search/SearchProgressTableModel.java @@ -7,6 +7,9 @@ import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class SearchProgressTableModel extends DefaultTableModel { @Serial @@ -36,7 +39,7 @@ public void update(String source, int queue, String release) { private void updateRow(int rowNr, int queue, String release) { this.setValueAt(queue, rowNr, 1); // Queue - this.setValueAt(release, rowNr, 2); // Release + this.setValueAt(release, rowNr, 2); // ReleaseWithoutPath } private void createRow(String source, int queue, String release) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java index 817eb80c..5313a31b 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ArrowButton.java @@ -5,7 +5,9 @@ import java.io.Serial; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class ArrowButton extends JButton { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java index 24a81938..53e1aed4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/BoxModelProperties.java @@ -1,14 +1,18 @@ package org.lodder.subtools.multisubdownloader.gui.extra; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@NullMarked public class BoxModelProperties { - @val Integer top; - @val Integer left; - @val Integer bottom; - @val Integer right; + @val @Nullable Integer top; + @val @Nullable Integer left; + @val @Nullable Integer bottom; + @val @Nullable Integer right; - public BoxModelProperties(Integer top=null, Integer left=null, Integer bottom=null, Integer right=null) { + public BoxModelProperties(@Nullable Integer top=null, @Nullable Integer left=null, @Nullable Integer bottom=null, + @Nullable Integer right=null) { this.top = top; this.left = left; this.bottom = bottom; @@ -23,7 +27,7 @@ public String getInsets() { return "insets %s %s %s %s".formatted(getValue(top), getValue(left), getValue(bottom), getValue(right)); } - private String getValue(Integer padding) { + private String getValue(@Nullable Integer padding) { return padding == null ? "n" : String.valueOf(padding); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java index 988b8098..fe515f88 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/ImageListCellRenderer.java @@ -4,9 +4,11 @@ import java.awt.*; import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class ImageListCellRenderer extends JLabel implements ListCellRenderer { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java index 521aa8ea..5fa96d25 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/JListWithImages.java @@ -11,7 +11,10 @@ import com.google.common.base.Objects; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@NullMarked public class JListWithImages extends JList> { @Serial @@ -46,11 +49,11 @@ public void removeSelectedItem() { } } - public T getObject(int index) { + public @Nullable T getObject(int index) { return getLabelPanel(index).map(LabelPanel::getObject).orElse(null); } - public Image getImage(int index) { + public @Nullable Image getImage(int index) { return getLabelPanel(index).map(LabelPanel::getImage).orElse(null); } @@ -66,6 +69,7 @@ private Optional> getLabelPanel(int index) { return Optional.ofNullable(getModel().getElementAt(index)); } + @NullMarked public static class LabelPanel extends JPanel { @Serial private static final long serialVersionUID = 1L; @@ -86,6 +90,7 @@ public Image getImage() { } } + @NullMarked private static class Label extends JLabel { @Serial private static final long serialVersionUID = 1L; @val T object; @@ -97,7 +102,7 @@ private static class Label extends JLabel { this.image = image; } - private static ImageIcon getImageIcon(Image image) { + private static ImageIcon getImageIcon(@Nullable Image image) { return image == null ? new ImageIcon() : resizeIcon(new ImageIcon(image), 20, 20); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java index 71e6bf63..39ddac97 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/LogTextAppender.java @@ -11,8 +11,10 @@ import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.encoder.Encoder; +import org.jspecify.annotations.NullMarked; import org.slf4j.LoggerFactory; +@NullMarked public class LogTextAppender extends AppenderBase { private final Encoder encoder = new EchoEncoder<>(); private final ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -49,11 +51,7 @@ public void append(ILoggingEvent event) { out.flush(); final String line = out.toString(StandardCharsets.UTF_8); - SwingUtilities.invokeLater(() -> { - if (jTextArea != null) { - jTextArea.append(line); - } - }); + SwingUtilities.invokeLater(() -> jTextArea.append(line)); out.reset(); } catch (IOException e) { throw new RuntimeException(e); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java index 77fba6e4..dede229f 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/MemoryFolderChooser.java @@ -1,20 +1,25 @@ package org.lodder.subtools.multisubdownloader.gui.extra; +import static util.Utils.*; + import javax.swing.*; import java.awt.*; import java.io.File; import java.nio.file.Path; -import java.util.Objects; import java.util.Optional; import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; +@NullMarked public class MemoryFolderChooser { - private static MemoryFolderChooser instance; + private static final LazySupplier instance = new LazySupplier<>(MemoryFolderChooser::new); private final JFileChooser chooser; - @var Path memory; + @var @Nullable Path memory; private MemoryFolderChooser() { chooser = new JFileChooser(); @@ -23,20 +28,17 @@ private MemoryFolderChooser() { } public static MemoryFolderChooser getInstance() { - if (instance == null) { - instance = new MemoryFolderChooser(); - } - return instance; + return instance.get(); } - public Optional selectDirectory(Component c, String title, Path path) { - return selectDirectory(c, title, path.toFile()); + public Optional selectDirectory(Component c, String title, @Nullable Path path) { + return selectDirectory(c, title, ifNotNull(path, Path::toFile)); } - public Optional selectDirectory(Component c, String title, File file) { + public Optional selectDirectory(Component c, String title, @Nullable File file) { chooser.setDialogTitle(title); - if (file == null || !StringUtils.isBlank(file.getAbsolutePath())) { - chooser.setCurrentDirectory(Objects.requireNonNullElseGet(memory.toFile(), () -> new File("."))); + if (file != null && !StringUtils.isBlank(file.getAbsolutePath())) { + chooser.setCurrentDirectory(ifNotNullOrElseGet(memory, Path::toFile, () -> new File("."))); } else { chooser.setCurrentDirectory(file); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java index 52ae5c4d..7f5486eb 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PanelCheckBox.java @@ -8,8 +8,10 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +@NullMarked public class PanelCheckBox extends JPanel { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java index 0157696b..18b82c64 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PartialDisableComboBox.java @@ -9,9 +9,12 @@ import java.util.List; import java.util.stream.IntStream; +import org.jspecify.annotations.NullMarked; + /** * @author author */ +@NullMarked public class PartialDisableComboBox extends JComboBox { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java index e8c3e2c2..ca1c8a7b 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/PopupListener.java @@ -4,8 +4,10 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; +@NullMarked public class PopupListener extends MouseAdapter { private final JPopupMenu popupMenu; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java index 083413ff..4a656507 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/TitlePanel.java @@ -6,8 +6,10 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +@NullMarked public class TitlePanel extends JPanel { @Serial private static final long serialVersionUID = 1L; @@ -18,7 +20,7 @@ public TitlePanel(String title, boolean fillContents=true, BoxModelProperties margin=new BoxModelProperties(), BoxModelProperties padding=new BoxModelProperties(), - LayoutManager panelLayout=null, + @Nullable LayoutManager panelLayout=null, String panelColumnConstraints="") { super(new MigLayout("fillx, nogrid, " + margin.getInsets())); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/Messenger.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/Messenger.java index 020c4981..a13f1595 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/Messenger.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/Messenger.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.gui.extra.progress; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface Messenger { void message(String message); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java index 14d72859..32b6c301 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusLabel.java @@ -3,6 +3,9 @@ import javax.swing.*; import java.io.Serial; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class StatusLabel extends JLabel implements Messenger { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java index 41f334d4..e8de65b3 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/progress/StatusMessenger.java @@ -4,7 +4,9 @@ import java.util.List; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public class StatusMessenger implements Messenger { @val static StatusMessenger instance = new StatusMessenger(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java index 08e657df..f71e073d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomColumnName.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public interface CustomColumnName { @val String columnName; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java index 5e775f5c..3b10bd32 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/CustomTable.java @@ -1,12 +1,15 @@ package org.lodder.subtools.multisubdownloader.gui.extra.table; -import javax.swing.table.*; +import javax.swing.table.TableColumn; import java.awt.event.MouseEvent; import java.io.Serial; import java.util.EnumMap; import java.util.Map; import java.util.stream.IntStream; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class CustomTable extends ZebraJTable { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java index 3139f1b7..b75e4fb5 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SearchColumnName.java @@ -7,8 +7,10 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum SearchColumnName implements CustomColumnName { RELEASE("App.Release", String.class, false), @@ -18,7 +20,6 @@ public enum SearchColumnName implements CustomColumnName { OBJECT("App.EpisodeObject", Object.class, false), SEASON("App.Season", String.class, false), EPISODE("App.Episode", String.class, false), - TYPE("SearchColumnName.Type", String.class, false), TITLE("SearchColumnName.Title", String.class, false), SOURCE("SearchColumnName.Source", String.class, false), SCORE("SearchColumnName.Score", Integer.class, false); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java index 7181dc63..87a9cc0f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableColumnName.java @@ -4,9 +4,11 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public enum SubtitleTableColumnName implements CustomColumnName { SELECT("App.Select", Boolean.class, true, _ -> false), SCORE("SubtitleTableColumnName.Score", Integer.class, false, Subtitle::getScore), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java index 6cd5472c..199db043 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/SubtitleTableModel.java @@ -2,13 +2,15 @@ import static org.lodder.subtools.multisubdownloader.gui.extra.table.SubtitleTableColumnName.*; -import javax.swing.table.*; +import javax.swing.table.DefaultTableModel; import java.io.Serial; import java.util.stream.IntStream; import java.util.stream.Stream; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SubtitleTableModel extends DefaultTableModel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java index bb6a4fbb..73e4190f 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/VideoTableModel.java @@ -17,30 +17,35 @@ import manifold.ext.props.rt.api.get; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.sublibrary.model.MovieRelease; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; +@NullMarked public class VideoTableModel extends DefaultTableModel { @Serial private static final long serialVersionUID = 1L; private static final List SHOW_COLUMNS = - List.of(TYPE, RELEASE, FILENAME, TITLE, SEASON, EPISODE, FOUND, SELECT, OBJECT); + List.of(RELEASE, FILENAME, TITLE, SEASON, EPISODE, FOUND, SELECT, OBJECT); private static final List SUBTITLE_COLUMNS = List.of(FILENAME, SOURCE, SCORE, SELECT, OBJECT); private static final Map SHOW_COLUMNS_INDEX = IntStream.range(0, SHOW_COLUMNS.size()) - .collect(() -> new EnumMap<>(SearchColumnName.class), (map, i) -> map.put(SHOW_COLUMNS.get(i), i), - (l, r) -> { - throw new IllegalArgumentException("Duplicate keys [$l] and [$r]"); - }); + .collect(() -> new EnumMap<>(SearchColumnName.class), (map, i) -> map.put(SHOW_COLUMNS.get(i), i), + (l, r) -> { + throw new IllegalArgumentException("Duplicate keys [$l] and [$r]"); + }); private final Class[] columnTypes; private final Boolean[] columnEditables; - private final Map rowMap = new LinkedHashMap<>(); + private final Map rowMap = new LinkedHashMap<>(); private boolean showOnlyFound = false; @var UserInteractionHandler userInteractionHandler; @@ -59,11 +64,11 @@ public static VideoTableModel getDefaultSubtitleTableModel() { return new VideoTableModel(SUBTITLE_COLUMNS); } - public void addRows(List l) { + public void addRows(List l) { l.forEach(this::addRow); } - public void addRow(Release release) { + public void addRow(ReleaseWithPath release) { /* If we try to add an existing release, we just have to update that row */ synchronized (this) { if (rowMap.containsKey(release)) { @@ -74,7 +79,7 @@ public void addRow(Release release) { return; } - if (!showOnlyFound || release.getMatchingSubCount() != 0) { + if (!showOnlyFound || release.matchingSubCount != 0) { Row row = createRow(release); rowMap.put(release, row); this.addRow(row.rowObject); @@ -86,10 +91,11 @@ private Row createRow(Release release) { return new Row(release, userInteractionHandler); } + @NullMarked private static class Row { private final Release release; private final UserInteractionHandler userInteractionHandler; - @get Vector rowObject; + @get Vector<@Nullable Object> rowObject; public Row(Release release, UserInteractionHandler userInteractionHandler) { this.release = release; @@ -99,22 +105,20 @@ public Row(Release release, UserInteractionHandler userInteractionHandler) { case TvRelease tvRelease -> tvRelease.originalName; case MovieRelease movieRelease -> movieRelease.name; }; - case FILENAME -> release.fileName; + case FILENAME -> release instanceof ReleaseWithPath r ? r.fileName : null; case FOUND -> calculateSubsFound(); case SELECT -> false; case OBJECT -> release; - case SEASON -> release instanceof TvRelease tvRelease ? tvRelease.season : null; - case EPISODE -> release instanceof TvRelease tvRelease ? tvRelease.firstEpisode : null; - case TYPE -> release.videoType; - case TITLE -> release instanceof TvRelease tvRelease ? tvRelease.title : null; + case SEASON -> release instanceof TvReleaseWithoutPath tvRelease ? tvRelease.season : null; + case EPISODE -> release instanceof TvReleaseWithoutPath tvRelease ? tvRelease.firstEpisode : null; + case TITLE -> release instanceof TvReleaseWithoutPath tvRelease ? tvRelease.title : null; default -> throw new IllegalArgumentException("Unexpected value: " + searchColumn); }).collect(Collectors.toCollection(Vector::new)); } private int calculateSubsFound() { return userInteractionHandler != null ? - userInteractionHandler.getAutomaticSelection(release.getMatchingSubs()).size() : - release.getMatchingSubCount(); + userInteractionHandler.getAutomaticSelection(release.matchingSubs).size() : release.matchingSubCount; } public int updateSubsFound() { @@ -166,12 +170,11 @@ public void removeRow(int i) { } public void removeShow(Release selectedShow) { - Iterator iterator = rowMap.keySet().iterator(); + Iterator iterator = rowMap.keySet().iterator(); int idx = -1; while (iterator.hasNext()) { idx++; - Release release = iterator.next(); - if (release == selectedShow) { + if (iterator.next() == selectedShow) { iterator.remove(); super.removeRow(idx); return; @@ -181,7 +184,7 @@ public void removeShow(Release selectedShow) { private void updateTable() { synchronized (this) { - List newRowList = new ArrayList<>(this.rowMap.keySet()); + List newRowList = new ArrayList<>(this.rowMap.keySet()); clearTable(); addRows(newRowList); } @@ -211,7 +214,7 @@ public int getSelectedAmountOfShows() { return (int) rowMap.values().stream().filter(Row::isSelected).count(); } - public List getSelectedShows() { + public List getSelectedShows() { return rowMap.entrySet().stream().filter(entry -> entry.getValue().isSelected()).map(Entry::getKey).toList(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java index 081a6f4e..9505f199 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/extra/table/ZebraJTable.java @@ -3,9 +3,12 @@ import java.awt.*; import java.io.Serial; +import org.jspecify.annotations.NullMarked; + /** * A JTable that draws a zebra striped background. */ +@NullMarked public class ZebraJTable extends javax.swing.JTable { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java index bc1b6bc9..7d0b9acf 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jpopupmenu/MyPopupMenu.java @@ -5,7 +5,9 @@ import java.io.Serial; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class MyPopupMenu extends JPopupMenu { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java index f116d497..d234f090 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordField.java @@ -7,38 +7,67 @@ import javax.swing.event.DocumentListener; import java.awt.*; import java.io.Serial; +import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; import manifold.ext.props.rt.api.var; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; -public class MyPasswordField extends JPasswordField implements MyPasswordFieldOthersIntf { +@NullMarked +public class MyPasswordField extends JPasswordField { @Serial private static final long serialVersionUID = 1L; private static final String DEFAULT_BORDER_PROPERTY = "DefaultBorder"; private static final Border ERROR_BORDER = new LineBorder(Color.RED, 1); - public Predicate valueVerifier = StringUtils::isNotEmpty; + public final Predicate valueVerifier; - private boolean requireValue; - private Consumer valueChangedCallbackListener; - private BooleanConsumer[] validityChangedCallbackListeners; + private @Nullable Consumer valueChangedCallbackListener; + private List validityChangedCallbackListeners; private final ObjectWrapper valueWrapper = new ObjectWrapper<>(); private final ObjectWrapper validWrapper = new ObjectWrapper<>(); - private Predicate completeValueVerifier; + private final Predicate completeValueVerifier; - private MyPasswordField() { + public MyPasswordField(boolean requireValue=false, Predicate verifier=StringUtils::isNotEmpty, + @Nullable Consumer valueChangedCallbackListener=null, + List validityChangedCallbackListeners=(List) List.of()) { super(); putClientProperty(DEFAULT_BORDER_PROPERTY, getBorder()); - } + this.valueVerifier = verifier; + this.valueChangedCallbackListener = valueChangedCallbackListener; + this.validityChangedCallbackListeners = validityChangedCallbackListeners; + + this.completeValueVerifier = + requireValue ? text -> (StringUtils.isNotEmpty(text) && valueVerifier.test(text)) : valueVerifier; - public static MyPasswordField builder() { - return new MyPasswordField(); + if (requireValue || valueChangedCallbackListener != null || !validityChangedCallbackListeners.isEmpty()) { + checkValidity(getRawText()); + getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + checkValidity(getRawText()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + checkValidity(getRawText()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + checkValidity(getRawText()); + } + + }); + } } @Override @@ -55,30 +84,7 @@ private void setSuperBorder(Border border) { super.setBorder(border); } - @Override - public MyPasswordField withValueVerifier(Predicate verifier) { - this.valueVerifier = verifier; - return this; - } - - @Override - public MyPasswordField requireValue(boolean requireValue) { - this.requireValue = requireValue; - return this; - } - - @Override - public MyPasswordField withValueChangedCallback(Consumer valueChangedCallbackListener) { - this.valueChangedCallbackListener = valueChangedCallbackListener; - return this; - } - - @Override - public MyPasswordField withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners) { - this.validityChangedCallbackListeners = validityChangedCallbackListeners; - return this; - } - + @NullMarked private static class ObjectWrapper { @var S value; } @@ -101,50 +107,13 @@ private static Border getDefaultBorder(JComponent thisTextField) { return (Border) thisTextField.getClientProperty(DEFAULT_BORDER_PROPERTY); } - @Override - public MyPasswordField build() { - if (valueVerifier != null && requireValue) { - completeValueVerifier = text -> (StringUtils.isNotEmpty(text) && valueVerifier.test(text)); - } else if (valueVerifier != null) { - completeValueVerifier = valueVerifier; - } else if (requireValue) { - completeValueVerifier = StringUtils::isNotEmpty; - } else { - completeValueVerifier = _ -> true; - } - - if (valueVerifier != null || requireValue || valueChangedCallbackListener != null || - validityChangedCallbackListeners != null) { - checkValidity(getRawText()); - getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - checkValidity(getRawText()); - } - - @Override - public void removeUpdate(DocumentEvent e) { - checkValidity(getRawText()); - } - - @Override - public void changedUpdate(DocumentEvent e) { - checkValidity(getRawText()); - } - - }); - } - return this; - } - private void checkValidity(String text) { boolean valid = completeValueVerifier.test(text); setSuperBorder(valid ? MyPasswordField.getDefaultBorder(this) : ERROR_BORDER); boolean changedValidity = Objects.equals(validWrapper.value, valid); validWrapper.value = valid; - if (changedValidity && validityChangedCallbackListeners != null) { + if (changedValidity) { validityChangedCallbackListeners.forEach(listener -> listener.accept(valid)); } @@ -162,7 +131,7 @@ private String getRawText() { } @Override - public String getText() { + public @Nullable String getText() { String text = new String(getPassword()); return completeValueVerifier.test(text) ? text : null; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java deleted file mode 100644 index 83bed150..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyPasswordFieldOthersIntf.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.util.function.Consumer; -import java.util.function.Predicate; - -import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; - -public interface MyPasswordFieldOthersIntf { - MyPasswordFieldOthersIntf withValueVerifier(Predicate verifier); - - default MyPasswordFieldOthersIntf requireValue() { - return requireValue(true); - } - - MyPasswordFieldOthersIntf requireValue(boolean requireValue); - - MyPasswordFieldOthersIntf withValueChangedCallback(Consumer valueChangedCallbackListener); - - MyPasswordFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners); - - MyPasswordField build(); -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java index fc8f5079..839d7b31 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldCommon.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; +import static util.Utils.*; + import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.LineBorder; @@ -7,19 +9,22 @@ import javax.swing.event.DocumentListener; import java.awt.*; import java.io.Serial; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import manifold.ext.rt.api.Self; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; -public abstract sealed class MyTextFieldCommon> extends JTextField implements - MyTextFieldToStringMapperIntf, - MyTextFieldToObjectMapperIntf, - MyTextFieldOthersIntf +@NullMarked +public abstract sealed class MyTextFieldCommon> + extends JTextField permits MyTextFieldInteger, MyTextFieldPath, MyTextFieldString { @Serial @@ -27,72 +32,82 @@ public abstract sealed class MyTextFieldCommon toStringMapper; - private Function toObjectMapper; - private Predicate valueVerifier; - private boolean requireValue; - private Consumer valueChangedCallbackListener; - private BooleanConsumer[] validityChangedCallbackListeners; + private final boolean requireValue; + private final Function toStringMapper; + private final Function toObjectMapper; + private final @Nullable Consumer valueChangedCallbackListener; + private List validityChangedCallbackListeners = new ArrayList<>(); private final ObjectWrapper valueWrapper = new ObjectWrapper<>(); private final ObjectWrapper validWrapper = new ObjectWrapper<>(); - private Predicate completeValueVerifier; + private final Predicate completeValueVerifier; - MyTextFieldCommon() { + MyTextFieldCommon(boolean requireValue=false, Function toStringMapper, + Function toObjectMapper, Predicate valueVerifier, + @Nullable Consumer valueChangedCallbackListener) { putClientProperty(DEFAULT_BORDER_PROPERTY, getBorder()); - } + this.requireValue = requireValue; + this.toStringMapper = toStringMapper; + this.toObjectMapper = toObjectMapper; - @Override - public void setBorder(Border border) { - setSuperBorder(border); - putClientProperty(DEFAULT_BORDER_PROPERTY, border); - } + this.valueChangedCallbackListener = valueChangedCallbackListener; - public void setErrorBorder() { - setBorder(ERROR_BORDER); - } + completeValueVerifier = + requireValue ? text -> (StringUtils.isNotEmpty(text) && valueVerifier.test(text)) : valueVerifier; - private void setSuperBorder(Border border) { - super.setBorder(border); + if (requireValue || valueChangedCallbackListener != null) { + configureCallback(); + } } - @Override - public R withToStringMapper(Function toStringMapper) { - this.toStringMapper = toStringMapper; - return self(); - } - @Override - public R withToObjectMapper(Function toObjectMapper) { - this.toObjectMapper = toObjectMapper; - return self(); + private void configureCallback() { + checkValidity(getText()); + getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + checkValidity(getText()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + checkValidity(getText()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + checkValidity(getText()); + } + + }); } - @Override - public R withValueVerifier(Predicate verifier) { - this.valueVerifier = verifier; - return self(); + + public @Self MyTextFieldCommon addValidityChangedCallbackListeners( + BooleanConsumer validityChangedCallbackListener) { + if (!requireValue && valueChangedCallbackListener == null && validityChangedCallbackListeners.isEmpty()) { + configureCallback(); + } + return this; } @Override - public R requireValue(boolean requireValue) { - this.requireValue = requireValue; - return self(); + public void setBorder(Border border) { + setSuperBorder(border); + putClientProperty(DEFAULT_BORDER_PROPERTY, border); } - @Override - public R withValueChangedCallback(Consumer valueChangedCallbackListener) { - this.valueChangedCallbackListener = valueChangedCallbackListener; - return self(); + public void setErrorBorder() { + setBorder(ERROR_BORDER); } - @Override - public final R withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners) { - this.validityChangedCallbackListeners = validityChangedCallbackListeners; - return self(); + private void setSuperBorder(Border border) { + super.setBorder(border); } - private static class ObjectWrapper { + @NullMarked + private static class ObjectWrapper { private S value; public boolean setValue(S value) { @@ -124,50 +139,13 @@ private static Border getDefaultBorder(JComponent thisTextField) { return (Border) thisTextField.getClientProperty(DEFAULT_BORDER_PROPERTY); } - @Override - public R build() { - if (valueVerifier != null && requireValue) { - completeValueVerifier = text -> (StringUtils.isNotEmpty(text) && valueVerifier.test(text)); - } else if (valueVerifier != null) { - completeValueVerifier = valueVerifier; - } else if (requireValue) { - completeValueVerifier = StringUtils::isNotEmpty; - } else { - completeValueVerifier = _ -> true; - } - - if (valueVerifier != null || requireValue || valueChangedCallbackListener != null || - validityChangedCallbackListeners != null) { - checkValidity(getText()); - getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - checkValidity(getText()); - } - - @Override - public void removeUpdate(DocumentEvent e) { - checkValidity(getText()); - } - - @Override - public void changedUpdate(DocumentEvent e) { - checkValidity(getText()); - } - - }); - } - return self(); - } - private void checkValidity(String text) { boolean valid = completeValueVerifier.test(text); setSuperBorder(valid ? MyTextFieldCommon.getDefaultBorder(self()) : ERROR_BORDER); boolean changedValidity = validWrapper.setValue(valid); - if (changedValidity && validityChangedCallbackListeners != null) { - Arrays.stream(validityChangedCallbackListeners).forEach(listener -> listener.accept(valid)); + if (changedValidity) { + validityChangedCallbackListeners.forEach(listener -> listener.accept(valid)); } if (valueChangedCallbackListener != null) { @@ -194,9 +172,9 @@ public Optional getOptionalObject() { } public void setObject(T object) { - super.setText(object == null ? null : toStringMapper.apply(object)); + super.setText(ifNotNull(object, toStringMapper::apply)); valueWrapper.setValue(object); - validWrapper.setValue(completeValueVerifier.test(object == null ? null : toStringMapper.apply(object))); + validWrapper.setValue(completeValueVerifier.test(toStringMapper.apply(object))); } public boolean hasValidValue() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java index 1b278057..ee12304b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldInteger.java @@ -1,17 +1,25 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; +import static util.Utils.*; + import java.io.Serial; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; -public final class MyTextFieldInteger extends MyTextFieldCommon { +@NullMarked +public final class MyTextFieldInteger extends MyTextFieldCommon<@Nullable Integer, MyTextFieldInteger> { @Serial private static final long serialVersionUID = 1L; - private static final Function TO_STRING_MAPPER = i -> i == null ? null : String.valueOf(i); - private static final Function TO_OBJECT_MAPPER = s -> s == null ? null : Integer.parseInt(s); + private static final Function<@Nullable Integer, @Nullable String> TO_STRING_MAPPER = + i -> ifNotNull(i, String::valueOf); + private static final Function<@Nullable String, @Nullable Integer> TO_OBJECT_MAPPER = + s -> ifNotNull(s, Integer::parseInt); public static final Predicate INT_VERIFIER = text -> { try { if (StringUtils.isBlank(text)) { @@ -24,14 +32,12 @@ public final class MyTextFieldInteger extends MyTextFieldCommon builder() { - return new MyTextFieldInteger() - .withToStringMapper(TO_STRING_MAPPER) - .withToObjectMapper(TO_OBJECT_MAPPER) - .withValueVerifier(INT_VERIFIER); + public MyTextFieldInteger( + boolean requireValue=false, + Function<@Nullable Integer, @Nullable String> toStringMapper=TO_STRING_MAPPER, + Function<@Nullable String, @Nullable Integer> toObjectMapper=TO_OBJECT_MAPPER, + Predicate valueVerifier=INT_VERIFIER, + @Nullable Consumer valueChangedCallbackListener=null) { + super(requireValue, toStringMapper, toObjectMapper, valueVerifier, valueChangedCallbackListener); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java deleted file mode 100644 index 81bbf3a8..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldOthersIntf.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.util.function.Consumer; -import java.util.function.Predicate; - -import org.lodder.subtools.sublibrary.util.function.BooleanConsumer; - -public interface MyTextFieldOthersIntf> { - MyTextFieldOthersIntf withValueVerifier(Predicate verifier); - - default MyTextFieldOthersIntf requireValue() { - return requireValue(true); - } - - MyTextFieldOthersIntf requireValue(boolean requireValue); - - MyTextFieldOthersIntf withValueChangedCallback(Consumer valueChangedCallbackListener); - - MyTextFieldOthersIntf withValidityChangedCallback(BooleanConsumer... validityChangedCallbackListeners); - - R build(); -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java index d4256921..b6943874 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldPath.java @@ -1,20 +1,26 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; +import static util.Utils.*; + import java.io.Serial; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; -public final class MyTextFieldPath extends MyTextFieldCommon { +@NullMarked +public final class MyTextFieldPath extends MyTextFieldCommon<@Nullable Path, MyTextFieldPath> { @Serial private static final long serialVersionUID = 1L; - private static final Function TO_STRING_MAPPER = - path -> path == null ? null : path.toAbsolutePath().toString(); - private static final Function TO_OBJECT_MAPPER = s -> s == null ? null : Path.of(s); + private static final Function<@Nullable Path, @Nullable String> TO_STRING_MAPPER = + path -> ifNotNull(path, p -> p.toAbsolutePath().toString()); + private static final Function<@Nullable String, @Nullable Path> TO_OBJECT_MAPPER = s -> ifNotNull(s, Path::of); public static final Predicate ABSOLUTE_PATH_VERIFIER = text -> { try { return StringUtils.isBlank(text) || Path.of(text).isAbsolute(); @@ -23,14 +29,12 @@ public final class MyTextFieldPath extends MyTextFieldCommon builder() { - return new MyTextFieldPath() - .withToStringMapper(TO_STRING_MAPPER) - .withToObjectMapper(TO_OBJECT_MAPPER) - .withValueVerifier(ABSOLUTE_PATH_VERIFIER); + public MyTextFieldPath( + boolean requireValue=false, + Function<@Nullable Path, @Nullable String> toStringMapper=TO_STRING_MAPPER, + Function<@Nullable String, @Nullable Path> toObjectMapper=TO_OBJECT_MAPPER, + Predicate valueVerifier=ABSOLUTE_PATH_VERIFIER, + @Nullable Consumer valueChangedCallbackListener=null) { + super(requireValue, toStringMapper, toObjectMapper, valueVerifier, valueChangedCallbackListener); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java index 89d27496..78c3b4c2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldString.java @@ -1,9 +1,14 @@ package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; import java.io.Serial; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked public final class MyTextFieldString extends MyTextFieldCommon { @Serial private static final long serialVersionUID = 1L; @@ -12,18 +17,12 @@ public final class MyTextFieldString extends MyTextFieldCommon TO_OBJECT_MAPPER = Function.identity(); public static final Predicate VERIFIER = _ -> true; - private MyTextFieldString() { - - } - - public static MyTextFieldOthersIntf builder() { - return new MyTextFieldString() - .withToStringMapper(TO_STRING_MAPPER) - .withToObjectMapper(TO_OBJECT_MAPPER) - .withValueVerifier(VERIFIER); - } - - public static MyTextFieldOthersIntf create() { - return builder().build(); + public MyTextFieldString( + boolean requireValue=false, + Function<@Nullable String, @Nullable String> toStringMapper=TO_STRING_MAPPER, + Function<@Nullable String, @Nullable String> toObjectMapper=TO_OBJECT_MAPPER, + Predicate valueVerifier=VERIFIER, + @Nullable Consumer valueChangedCallbackListener=null) { + super(requireValue, toStringMapper, toObjectMapper, valueVerifier, valueChangedCallbackListener); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToObjectMapperIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToObjectMapperIntf.java deleted file mode 100644 index b22a12f8..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToObjectMapperIntf.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.util.function.Function; - -public interface MyTextFieldToObjectMapperIntf> { - MyTextFieldOthersIntf withToObjectMapper(Function toObjectMapper); -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToStringMapperIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToStringMapperIntf.java deleted file mode 100644 index c9b55976..00000000 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/jcomponent/jtextfield/MyTextFieldToStringMapperIntf.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield; - -import java.util.function.Function; - -public interface MyTextFieldToStringMapperIntf> { - MyTextFieldToObjectMapperIntf withToStringMapper(Function toStringMapper); -} diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java index 1affbc03..19ff745b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/InputPanel.java @@ -7,9 +7,11 @@ import java.io.Serial; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.SearchAction; import org.lodder.subtools.sublibrary.Language; +@NullMarked public abstract sealed class InputPanel extends JPanel permits SearchFileInputPanel, SearchTextInputPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java index d8b1b0f9..5cb0eb58 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/LoggingPanel.java @@ -8,8 +8,10 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.LogTextAppender; +@NullMarked public class LoggingPanel extends JPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java index 96dd8254..779b47d2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/ResultPanel.java @@ -3,15 +3,17 @@ import static org.lodder.subtools.multisubdownloader.Messages.*; import javax.swing.*; -import javax.swing.table.*; +import javax.swing.table.DefaultTableModel; import java.awt.event.ActionListener; import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; import org.lodder.subtools.multisubdownloader.gui.extra.table.SearchColumnName; import org.lodder.subtools.multisubdownloader.gui.extra.table.VideoTableModel; +@NullMarked public class ResultPanel extends JPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java index ac7f9848..e86bb223 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchFileInputPanel.java @@ -7,7 +7,9 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; +@NullMarked public final class SearchFileInputPanel extends InputPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java index 180950be..39c48bd1 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchPanel.java @@ -5,7 +5,9 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; +@NullMarked public class SearchPanel extends JPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java index fe459f35..26133da2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/SearchTextInputPanel.java @@ -7,8 +7,12 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.model.VideoSearchType; +@NullMarked public final class SearchTextInputPanel extends InputPanel { @Serial @@ -92,8 +96,8 @@ public int getEpisode() { return episode; } - public String getQuality() { - return txtQualityVersion.getText().trim(); + public @Nullable String getQuality() { + return StringUtils.trimToNull(txtQualityVersion.getText()); } public String getReleaseName() { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java index a61d3b78..d89c3921 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/DefaultSelectionPanel.java @@ -2,6 +2,7 @@ import static java.util.function.Predicate.*; import static org.lodder.subtools.multisubdownloader.Messages.*; +import static util.Utils.*; import javax.swing.*; import javax.swing.table.DefaultTableModel; @@ -13,10 +14,13 @@ import java.util.stream.Stream; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.gui.extra.ArrowButton; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; +@NullMarked public class DefaultSelectionPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 1L; @@ -43,6 +47,7 @@ public DefaultSelectionPanel(SettingsControl settingsCtrl) { loadPreferenceSettings(); } + @NullMarked private static class ScrollTable extends Container { @Serial private static final long serialVersionUID = 1L; @@ -59,12 +64,12 @@ public ScrollTable(String header, Class elementType) { this(header, (Collection) null); } - public ScrollTable(String header, Collection items) { - this(header, items == null ? null : items.stream()); + public ScrollTable(String header, @Nullable Collection items) { + this(header, (Stream) ifNotNull(items, Collection::stream)); } - private ScrollTable(String header, Stream items) { - this.table = new JTable(new DefaultTableModel(new String[]{ header }, 1)); + private ScrollTable(String header, @Nullable Stream items) { + this.table = new JTable(new DefaultTableModel(new String[]{header}, 1)); this.scrollPane = new JScrollPane().viewportView(table); this.model = (DefaultTableModel) table.getModel(); model.removeRow(0); @@ -75,7 +80,7 @@ private ScrollTable(String header, Stream items) { } public void addItem(E item) { - model.addRow(new Object[]{ item }); + model.addRow(new Object[]{item}); } public int getSelectedRow() { @@ -136,7 +141,7 @@ public List getItems() { @Override public Component[] getComponents() { - return new Component[]{ scrollPane, table }; + return new Component[]{scrollPane, table}; } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java index 4888fa41..5fa6960a 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/EpisodeLibraryPanel.java @@ -2,11 +2,13 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public final class EpisodeLibraryPanel extends VideoLibraryPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java index 8fc918a5..93c475e6 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/GeneralPanel.java @@ -10,6 +10,7 @@ import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; @@ -27,6 +28,7 @@ import org.lodder.subtools.multisubdownloader.settings.model.UpdateType; import org.lodder.subtools.sublibrary.Language; +@NullMarked public class GeneralPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 1L; @@ -154,16 +156,13 @@ public GeneralPanel(GUI gui, SettingsControl settingsCtrl) { ) .addToPanel(proxyPanel) .addComponent(new JLabel(getText("PreferenceDialog.Hostname"))) - .addComponent("wrap", - this.txtProxyHost = MyTextFieldString.builder().requireValue().build().columns(30)) + .addComponent("wrap", this.txtProxyHost = new MyTextFieldString(true).columns(30)) .addComponent(new JLabel(getText("PreferenceDialog.Port"))) - .addComponent(this.txtProxyPort = MyTextFieldInteger.builder().requireValue().build().columns(5)); + .addComponent(this.txtProxyPort = new MyTextFieldInteger(true).columns(5)); } - loadPreferenceSettings(); - } + // loadPreferenceSettings - public void loadPreferenceSettings() { cbxLanguage.setSelectedItem(settingsCtrl.settings.language); defaultIncomingFoldersList.addItems(PathMatchType.FOLDER.image, settingsCtrl.settings.defaultIncomingFolders); settingsCtrl.settings.excludeList.forEach(pathOrRegex -> excludeList.addItem(pathOrRegex.image, pathOrRegex)); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java index 2e4071c1..63c1e49b 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/MovieLibraryPanel.java @@ -2,11 +2,13 @@ import java.io.Serial; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public final class MovieLibraryPanel extends VideoLibraryPanel { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java index 10f8efec..e3d7981e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/OptionsPanel.java @@ -6,12 +6,14 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; import org.lodder.subtools.multisubdownloader.settings.model.SettingsProcessEpisodeSource; +@NullMarked public class OptionsPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/PreferencePanelIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/PreferencePanelIntf.java index 5457d411..2b33d5b9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/PreferencePanelIntf.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/PreferencePanelIntf.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.gui.panels.preference; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface PreferencePanelIntf { boolean hasValidSettings(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java index 383a9bad..6a0876d2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SerieProvidersPanel.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.JListWithImages; import org.lodder.subtools.multisubdownloader.gui.extra.JListWithImages.LabelPanel; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; @@ -20,6 +21,7 @@ import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubtitlesApi; +@NullMarked public class SerieProvidersPanel extends JPanel implements PreferencePanelIntf { @Serial @@ -64,11 +66,9 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { panelLayout:new MigLayout("insets 0, novisualpadding") ) .addComponent(new JLabel(getText("PreferenceDialog.Username"))) - .addComponent("wrap", this.txtAddic7edUsername = - MyTextFieldString.builder().requireValue().build().columns(20)) + .addComponent("wrap", this.txtAddic7edUsername = new MyTextFieldString(true).columns(20)) .addComponent(new JLabel(getText("PreferenceDialog.Password"))) - .addComponent(this.txtAddic7edPassword = - MyPasswordField.builder().requireValue().build().columns(20))); + .addComponent(this.txtAddic7edPassword = new MyPasswordField(true).columns(20))); // TV SUBTITLES this.chkSourceTvSubtitles = new JCheckBox("Tv Subtitles").addTo(titlePanel, "wrap"); @@ -87,11 +87,9 @@ public SerieProvidersPanel(SettingsControl settingsCtrl) { panelLayout:new MigLayout("insets 0, novisualpadding") ) .addComponent(new JLabel(getText("PreferenceDialog.Username"))) - .addComponent("wrap", txtOpenSubtitlesUsername = - MyTextFieldString.builder().requireValue().build().columns(20)) + .addComponent("wrap", txtOpenSubtitlesUsername = new MyTextFieldString(true).columns(20)) .addComponent(new JLabel(getText("PreferenceDialog.Password"))) - .addComponent(txtOpenSubtitlesPassword = - MyPasswordField.builder().requireValue().build().columns(20))); + .addComponent(txtOpenSubtitlesPassword = new MyPasswordField(true).columns(20))); // SUBDL this.chkSourceSubdl = new JCheckBox("SubDL").addTo(titlePanel, "wrap"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java index 2e087605..a68cd477 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFilePanel.java @@ -19,6 +19,7 @@ import java.util.stream.Stream; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; @@ -31,6 +32,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class StructureFilePanel extends JPanel { @Serial private static final long serialVersionUID = 1L; @@ -55,8 +57,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, .addToPanel(this, "span, grow"); new JLabel(getText("PreferenceDialog.Structure")).addTo(titlePanel, "shrink"); - this.txtFileStructure = - MyTextFieldString.builder().requireValue().build().columns(20).addTo(titlePanel, "grow"); + this.txtFileStructure = new MyTextFieldString(true).columns(20).addTo(titlePanel, "grow"); new JButton(getText("StructureBuilderDialog.Structure")) .actionListener(() -> { StructureBuilderDialog sDialog = @@ -100,7 +101,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, int id = langId.getAndIncrement(); JComboBox cmbLanguage = new JComboBox<>(Language.values()) .toMessageStringRenderer(Language::getMsgCode).addTo(languagePanel); - MyTextFieldString txtLanguage = MyTextFieldString.builder().build().columns(20).addTo(languagePanel); + MyTextFieldString txtLanguage = new MyTextFieldString(true).columns(20).addTo(languagePanel); JButton btnDelete = new JButton(getText("StructureFilePanel.Delete")) .actionListenerSelf(delBtn -> { languagePanel.remove(cmbLanguage); @@ -126,6 +127,7 @@ public StructureFilePanel(LibrarySettings librarySettings, VideoType videoType, loadPreferenceSettings(); } + @NullMarked private record LanguageComponents(JComboBox cmbLanguage, MyTextFieldString txtLanguage, JButton btnDelete) { @@ -180,6 +182,7 @@ public void savePreferenceSettings() { librarySettings.langCodeMap = languageMapping.toSettingsMap(); } + @NullMarked private static class LanguageMapping { private final Map languageComponentsMap = new LinkedHashMap<>(); private static final String DEFAULT_BORDER_PROPERTY = "DefaultBorder"; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java index d9237804..25be4c7e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/StructureFolderPanel.java @@ -7,6 +7,7 @@ import java.util.function.Function; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.dialog.StructureBuilderDialog; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; @@ -20,6 +21,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class StructureFolderPanel extends JPanel implements PreferencePanelIntf { @Serial @@ -46,8 +48,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType .addToPanel(this, "span, grow"); new JLabel(getText("PreferenceDialog.Location")).addTo(titlePanel, "shrink"); - this.txtLibraryFolder = - MyTextFieldPath.builder().requireValue().build().columns(20).addTo(titlePanel, "grow"); + this.txtLibraryFolder = new MyTextFieldPath(true).columns(20).addTo(titlePanel, "grow"); new JButton(getText("App.Browse")) .actionListener(() -> MemoryFolderChooser.getInstance() .selectDirectory(getRootPane(), getText("PreferenceDialog.LibraryFolder")) @@ -55,8 +56,7 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType .addTo(titlePanel, "shrink, wrap"); new JLabel(getText("StructureBuilderDialog.Structure")).addTo(titlePanel, "shrink"); - this.txtFolderStructure = - MyTextFieldString.builder().requireValue().build().columns(20).disabled().addTo(titlePanel, "grow"); + this.txtFolderStructure = new MyTextFieldString(true).columns(20).disabled().addTo(titlePanel, "grow"); JButton btnStructure = new JButton(getText("StructureBuilderDialog.Structure")) .actionListener(() -> { StructureBuilderDialog sDialog = new StructureBuilderDialog(null, @@ -82,7 +82,8 @@ public StructureFolderPanel(LibrarySettings librarySettings, VideoType videoType .addComponent(this.cbxReplaceSpaceChar = JComboBox.create('-', '.', '_')); // behaviour - txtLibraryFolder.withValidityChangedCallback(txtFolderStructure::setEnabled, btnStructure::setEnabled); + txtLibraryFolder.addValidityChangedCallbackListeners(txtFolderStructure::setEnabled) + .addValidityChangedCallbackListeners(btnStructure::setEnabled); loadPreferenceSettings(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java index 704b0551..ea01f9ae 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/SubtitleBackupPanel.java @@ -6,6 +6,7 @@ import java.io.Serial; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.gui.extra.PanelCheckBox; @@ -13,6 +14,7 @@ import org.lodder.subtools.multisubdownloader.gui.jcomponent.jtextfield.MyTextFieldPath; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; +@NullMarked public class SubtitleBackupPanel extends JPanel implements PreferencePanelIntf { @Serial private static final long serialVersionUID = 1L; @@ -33,7 +35,7 @@ public SubtitleBackupPanel(LibrarySettings librarySettings) { .addToPanel(this, "span, growx"); { - this.txtBackupSubtitlePath = MyTextFieldPath.builder().requireValue().build().columns(20); + this.txtBackupSubtitlePath = new MyTextFieldPath(true).columns(20); new PanelCheckBox( checkbox:this.chkBackupSubtitle = new JCheckBox(getText("PreferenceDialog.BackupSubtitles")), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java index b43a0786..992e60bd 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/panels/preference/VideoLibraryPanel.java @@ -7,6 +7,7 @@ import manifold.ext.props.rt.api.val; import net.miginfocom.swing.MigLayout; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.BoxModelProperties; import org.lodder.subtools.multisubdownloader.gui.extra.PartialDisableComboBox; import org.lodder.subtools.multisubdownloader.gui.extra.TitlePanel; @@ -17,6 +18,7 @@ import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public abstract sealed class VideoLibraryPanel extends JPanel implements PreferencePanelIntf permits EpisodeLibraryPanel, MovieLibraryPanel { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java index 7986cfb8..3192bf25 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/DownloadWorker.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.GUI; import org.lodder.subtools.multisubdownloader.Messages; import org.lodder.subtools.multisubdownloader.UserInteractionHandlerGUI; @@ -17,7 +18,7 @@ import org.lodder.subtools.multisubdownloader.lib.Info; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +27,7 @@ * Created by IntelliJ IDEA. User: lodder Date: 4/12/11 Time: 8:52 AM To change this template use Path | Settings | Path * Templates. */ +@NullMarked public class DownloadWorker extends SwingWorker implements Cancelable { private static final Logger LOGGER = LoggerFactory.getLogger(DownloadWorker.class); @@ -50,11 +52,11 @@ protected Void doInBackground() { Info.downloadOptions(settings, false); model.executedSynchronized(() -> { - List selectedShows = model.getSelectedShows(); + List selectedShows = model.getSelectedShows(); int selectedCount = selectedShows.size(); int progress = 0; int k = 0; - for (Release selectedShow : selectedShows) { + for (ReleaseWithPath selectedShow : selectedShows) { k++; if (k > 0) { progress = 100 * k / selectedCount; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java index 27cbad28..526b0af1 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/gui/workers/RenameWorker.java @@ -3,17 +3,21 @@ import javax.swing.*; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; -import org.lodder.subtools.multisubdownloader.actions.RenameAction; +import org.lodder.subtools.multisubdownloader.actions.MoveAndRenameAction; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; import org.lodder.subtools.multisubdownloader.gui.extra.progress.StatusMessenger; import org.lodder.subtools.multisubdownloader.gui.extra.table.CustomTable; import org.lodder.subtools.multisubdownloader.gui.extra.table.VideoTableModel; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public class RenameWorker extends SwingWorker implements Cancelable { private final CustomTable table; @@ -34,11 +38,11 @@ protected Void doInBackground() { final VideoTableModel model = (VideoTableModel) table.getModel(); model.executedSynchronized(() -> { - List selectedShows = model.getSelectedShows(); + List selectedShows = model.getSelectedShows(); int selectedCount = selectedShows.size(); int progress = 0; int k = 0; - for (Release selectedShow : selectedShows) { + for (ReleaseWithPath selectedShow : selectedShows) { k++; if (k > 0) { progress = 100 * k / selectedCount; @@ -48,11 +52,13 @@ protected Void doInBackground() { } setProgress(progress); - RenameAction renameAction = switch (selectedShow.videoType) { - case EPISODE -> new RenameAction(settings.episodeLibrarySettings, manager, userInteractionHandler); - case MOVIE -> new RenameAction(settings.movieLibrarySettings, manager, userInteractionHandler); + MoveAndRenameAction moveAndRenameAction = switch (selectedShow) { + case TvReleaseWithPath _ -> + new MoveAndRenameAction(settings.episodeLibrarySettings, manager, userInteractionHandler); + case MovieReleaseWithPath _ -> + new MoveAndRenameAction(settings.movieLibrarySettings, manager, userInteractionHandler); }; - renameAction.rename(selectedShow.getPath().resolve(selectedShow.fileName), selectedShow); + moveAndRenameAction.moveAndRename(selectedShow.path.resolve(selectedShow.fileName), selectedShow); model.removeShow(selectedShow); } }); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java index de467657..d2dd03f3 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/Info.java @@ -1,11 +1,13 @@ package org.lodder.subtools.multisubdownloader.lib; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.model.SubtitleSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings("java:S106") +@NullMarked public class Info { private static final Logger LOGGER = LoggerFactory.getLogger(Info.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java index 08a07ece..38eb2d22 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/ReleaseFactory.java @@ -3,40 +3,63 @@ import java.nio.file.Path; import java.util.Optional; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.control.MovieReleaseControl; import org.lodder.subtools.multisubdownloader.lib.control.TvReleaseControl; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; -import org.lodder.subtools.sublibrary.exception.ReleaseParseException; -import org.lodder.subtools.sublibrary.model.MovieRelease; -import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithoutPath; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public record ReleaseFactory(Settings settings, Manager manager) { private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseFactory.class); - public Optional createRelease(Path file, UserInteractionHandler userInteractionHandler, - boolean validate=true) { + public Optional createRelease(Path file, + UserInteractionHandler userInteractionHandler, boolean validate=true) { try { - Optional release = ReleaseParser.parse(file); + Optional release = ReleaseParser.parse(file); if (validate && release.isPresent()) { switch (release.get()) { - case TvRelease tvRelease -> + case TvReleaseWithPath tvRelease -> new TvReleaseControl(settings, manager, userInteractionHandler).process(tvRelease); - case MovieRelease movieRelease -> + case MovieReleaseWithPath movieRelease -> new MovieReleaseControl(settings, manager, userInteractionHandler).process(movieRelease); } } return release; - } catch (ReleaseParseException | ReleaseControlException e) { + } catch (ReleaseControlException e) { LOGGER.error("Failed to create a release for $file: " + e.getMessage(), e); return Optional.empty(); } } + + public Optional createRelease(String name, + UserInteractionHandler userInteractionHandler, boolean validate=true) { + try { + Optional release = ReleaseParser.parse(name); + if (validate && release.isPresent()) { + switch (release.get()) { + case TvReleaseWithoutPath tvRelease -> + new TvReleaseControl(settings, manager, userInteractionHandler).process(tvRelease); + case MovieReleaseWithoutPath movieRelease -> + new MovieReleaseControl(settings, manager, userInteractionHandler).process(movieRelease); + } + } + return release; + } catch (ReleaseControlException e) { + LOGGER.error("Failed to create a release for $name: " + e.getMessage(), e); + return Optional.empty(); + } + } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java index 7ac2f9bc..15a75029 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/MovieReleaseControl.java @@ -1,18 +1,20 @@ package org.lodder.subtools.multisubdownloader.lib.control; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.omdb.OmdbAdapter; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; -import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.ProviderIdType; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class MovieReleaseControl extends ReleaseControl { +@NullMarked +public final class MovieReleaseControl extends ReleaseControl { private final OmdbAdapter omdbAdapter; private static final Logger LOGGER = LoggerFactory.getLogger(MovieReleaseControl.class); @@ -23,7 +25,7 @@ public MovieReleaseControl(Settings settings, Manager manager, UserInteractionHa } @Override - public MovieRelease process(MovieRelease release) throws ReleaseControlException { + public MovieReleaseWithoutPath process(MovieReleaseWithoutPath release) throws ReleaseControlException { if (StringUtils.isBlank(release.name)) { throw new ReleaseControlException("Unable to extract title, check file", release); } @@ -34,7 +36,7 @@ public MovieRelease process(MovieRelease release) throws ReleaseControlException return release; } - private void setImdbId(MovieRelease release) { + private void setImdbId(MovieReleaseWithoutPath release) { release.providerIds.getImdbId().ifNotPresent(() -> omdbAdapter.searchMovie(release.name) .ifPresent(omdbRelease -> release.providerIds.add(ProviderIdType.IMDB, omdbRelease.imdbID))); release.providerIds.getImdbId().ifNotPresent(() -> imdbAdapter.getImdbId(release.name, VideoType.MOVIE) @@ -46,7 +48,7 @@ private void setImdbId(MovieRelease release) { } } - private void setTvdbId(MovieRelease release) { + private void setTvdbId(MovieReleaseWithoutPath release) { release.providerIds.getTvdbId().ifNotPresent(() -> tvdbAdapter.searchMovie(release.name) .ifPresent(movie -> release.providerIds.add(ProviderIdType.TVDB, movie.id))); // TODO enable this, also use imdbId if present @@ -54,7 +56,7 @@ private void setTvdbId(MovieRelease release) { // .ifPresent(imdbDetails -> release.providerIds.add(ProviderIdType.TVDB, imdbDetails.tvdbId))); } - private void processInfo(MovieRelease release) { + private void processInfo(MovieReleaseWithoutPath release) { release.providerIds.getImdbId().ifPresentOrElse( imdbId -> imdbAdapter.getDetails(imdbId).ifPresent(details -> { release.year = details.year; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java index 37b7b420..80b6d718 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/ReleaseControl.java @@ -3,16 +3,19 @@ import static manifold.ext.props.rt.api.PropOption.*; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.imdb.ImdbAdapter; import org.lodder.subtools.sublibrary.data.omdb.OmdbAdapter; import org.lodder.subtools.sublibrary.data.tvdb.TvdbAdapter; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; -public abstract sealed class ReleaseControl permits MovieReleaseControl, TvReleaseControl { +@NullMarked +public abstract sealed class ReleaseControl + permits MovieReleaseControl, TvReleaseControl { @val(Protected) Settings settings; @val(Protected) Manager manager; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java index b2eb7965..0462aae3 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/TvReleaseControl.java @@ -1,27 +1,28 @@ package org.lodder.subtools.multisubdownloader.lib.control; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; import org.lodder.subtools.sublibrary.model.ProviderIdType; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.VideoType; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class TvReleaseControl extends ReleaseControl { +@NullMarked +public final class TvReleaseControl extends ReleaseControl { private static final Logger LOGGER = LoggerFactory.getLogger(TvReleaseControl.class); - public TvReleaseControl(Settings settings, Manager manager, - UserInteractionHandler userInteractionHandler) { + public TvReleaseControl(Settings settings, Manager manager, UserInteractionHandler userInteractionHandler) { super(settings, manager, userInteractionHandler); } @Override - public TvRelease process(TvRelease release) throws ReleaseControlException { + public TvReleaseWithoutPath process(TvReleaseWithoutPath release) throws ReleaseControlException { if (StringUtils.isBlank(release.name)) { throw new ReleaseControlException("Unable to extract episode details, check file", release); } @@ -33,7 +34,7 @@ public TvRelease process(TvRelease release) throws ReleaseControlException { return release; } - private void setImdbId(TvRelease release) { + private void setImdbId(TvReleaseWithoutPath release) { release.providerIds.getImdbId().ifNotPresent(() -> imdbAdapter.getImdbId(release.name, VideoType.EPISODE) .ifPresent(imdbId -> release.providerIds.add(ProviderIdType.IMDB, imdbId))); release.providerIds.getImdbId().ifNotPresent(() -> omdbAdapter.searchSerie(release.name) @@ -45,22 +46,22 @@ private void setImdbId(TvRelease release) { } } - private void setTvdbId(TvRelease release) { + private void setTvdbId(TvReleaseWithoutPath release) { release.providerIds.getTvdbId().ifNotPresent(() -> tvdbAdapter.searchSerie(release.name, release.providerIds) .map(serie -> serie.providerId) .ifPresent(tvdbId -> release.providerIds.add(ProviderIdType.TVDB, Integer.parseInt(tvdbId)))); if (release.providerIds.getTvdbId().isEmpty()) { - throw new IllegalStateException("Unable to find TVDB id for movie: " + release.name); +// throw new IllegalStateException("Unable to find TVDB id for movie: " + release.name); } } - private void processTvdbInfo(TvRelease release) { + private void processTvdbInfo(TvReleaseWithoutPath release) { release.providerIds.getTvdbId() - .flatMap(tvdbId -> tvdbAdapter.searchEpisode(tvdbId, release.season, release.firstEpisode)) + .flatMapToObj(tvdbId -> tvdbAdapter.searchEpisode(tvdbId, release.season, release.firstEpisode)) .ifPresent(episode -> release.title = episode.name); } - private void processImdbInfo(TvRelease release) { + private void processImdbInfo(TvReleaseWithoutPath release) { // TODO implement this // release.providerIds.getImdbId().ifPresent( // imdbId -> imdbAdapter.getSerieDetails(imdbId) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java index 80eb6944..129c74ad 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFiltering.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.ExactNameFilter; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.KeywordFilter; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters.ReleaseGroupFilter; @@ -8,6 +9,7 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SubtitleFiltering { private final Settings settings; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java index 98a9d7fc..b8a581ee 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ExactNameFilter.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.regex.Pattern; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.cache.LRUMap; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -10,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public final class ExactNameFilter extends SubtitleFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ExactNameFilter.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java index 48266ab9..d0b82da2 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/KeywordFilter.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -7,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public final class KeywordFilter extends SubtitleFilter { private static final Logger LOGGER = LoggerFactory.getLogger(KeywordFilter.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java index 8e12f36e..5e167525 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/ReleaseGroupFilter.java @@ -1,12 +1,14 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleMatchType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public final class ReleaseGroupFilter extends SubtitleFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseGroupFilter.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java index c42ab90f..7385cbc7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/filters/SubtitleFilter.java @@ -1,10 +1,12 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.filters; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public abstract sealed class SubtitleFilter permits ExactNameFilter, KeywordFilter, ReleaseGroupFilter { public abstract boolean useSubtitle(Release release, Subtitle subtitle); @@ -14,7 +16,7 @@ public boolean excludeSubtitle(Release release, Subtitle subtitle) { } protected String getReleaseName(Release release) { - return release.fileName == null ? "" : StringUtils.substringBeforeLast(release.fileName, "."); + return StringUtils.substringBeforeLast(release.fileNameOrName, "."); } protected boolean checkKeywordSubtitleMatch(Subtitle subtitle, String keywordsFile) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java index 9eb701c9..5bb41ac9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculator.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class ScoreCalculator { private final SortWeight weights; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java index 50855a3b..7b4c5df1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeight.java @@ -11,11 +11,13 @@ import manifold.ext.props.rt.api.get; import manifold.ext.props.rt.api.set; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.replacers.GroupReplacer; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.replacers.KeywordReplacer; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public class SortWeight { private static final List KEYWORD_REPLACERS = List.of(new GroupReplacer()); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java index 5bf6aa30..01b20f74 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SubtitleComparator.java @@ -4,8 +4,10 @@ import java.io.Serializable; import java.util.Comparator; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SubtitleComparator implements Comparator, Serializable { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java index be7a412b..5c35e602 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacer.java @@ -3,8 +3,10 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public class GroupReplacer implements KeywordReplacer { @Override public void replace(Release release, Map weights) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/KeywordReplacer.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/KeywordReplacer.java index 5ea50490..e4310dc7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/KeywordReplacer.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/KeywordReplacer.java @@ -2,8 +2,10 @@ import java.util.Map; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public interface KeywordReplacer { void replace(Release release, Map weights); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java index 79415d3c..c9710d87 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/FilenameLibraryBuilder.java @@ -4,6 +4,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.multisubdownloader.settings.model.structure.MovieStructureTag; @@ -12,11 +13,15 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.tvdb.TvdbAdapter; import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public final class FilenameLibraryBuilder extends LibraryBuilder { private final String structure; @@ -27,7 +32,7 @@ public final class FilenameLibraryBuilder extends LibraryBuilder { private final boolean rename; public FilenameLibraryBuilder(String structure, boolean replaceSpace, char replacingSpaceChar, - boolean includeLanguageCode, Map languageTags, TvdbAdapter tvdbAdapter=null, + boolean includeLanguageCode, Map languageTags, @Nullable TvdbAdapter tvdbAdapter=null, boolean rename) { super(tvdbAdapter); this.structure = structure; @@ -50,68 +55,93 @@ public static FilenameLibraryBuilder fromSettings(LibrarySettings libSettings, M rename:libSettings.hasAnyLibraryAction(LibraryActionType.RENAME, LibraryActionType.MOVE_AND_RENAME)); } + /** + * Builds a relative Path (i.e. the new file name) based on a release object. + * + * @param release The ReleaseWithPath object. + * @return The new file name as a path + */ + @Override + public Path buildPath(ReleaseWithPath release) { + if (rename && StringUtils.isNotBlank(structure)) { + return Path.of(switch (release) { + case TvReleaseWithPath tvRelease -> buildEpisodeFolderStructure(tvRelease); + case MovieReleaseWithPath movieRelease -> buildMovieFolderStructure(movieRelease); + }); + } + return release.path.fileName; + } + @Override - public Path build(Release release) { + public String buildPathStructure(Release release) { if (rename && StringUtils.isNotBlank(structure)) { - String filename = switch (release) { - case TvRelease tvRelease -> { - String fName = structure; - // order is important! - fName = replace(fName, SerieStructureTag.SHOW_NAME, getShowName(tvRelease.name)); - fName = - replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); - fName = replaceFormattedEpisodeNumber(fName, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, - false); - fName = replace(fName, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); - fName = replace(fName, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); - fName = replace(fName, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); - fName = - replace(fName, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); - fName = replace(fName, SerieStructureTag.TITLE, tvRelease.title); - fName = replace(fName, SerieStructureTag.QUALITY, release.quality); - fName = replace(fName, SerieStructureTag.RELEASE_GROUP, release.releaseGroup); - - fName += "." + StringUtils.substringAfterLast(release.fileName, "."); - yield fName; - } - case MovieRelease movieRelease -> { - String fName = structure; - // order is important! - fName = replace(fName, MovieStructureTag.MOVIE_TITLE, getShowName(movieRelease.name)); - fName = replace(fName, MovieStructureTag.YEAR, formatNumber(movieRelease.year, false)); - fName = replace(fName, MovieStructureTag.QUALITY, release.quality); - fName = replace(fName, MovieStructureTag.RELEASE_GROUP, release.releaseGroup); - - fName += "." + StringUtils.substringAfterLast(release.fileName, "."); - yield fName; - } + return switch (release) { + case TvRelease tvRelease -> buildEpisodeFolderStructure(tvRelease); + case MovieRelease movieRelease -> buildMovieFolderStructure(movieRelease); }; + } + return release.fileNameOrName; + } - filename = filename.removeIllegalWindowsChars(); - if (replaceSpace) { - filename = filename.replace(' ', replacingSpaceChar); - } - return Path.of(filename); - } else { - return Path.of(release.fileName); + private String buildEpisodeFolderStructure(TvRelease tvRelease) { + String filename = structure; + // order is important! + filename = replace(filename, SerieStructureTag.SHOW_NAME, getShowName(tvRelease.name)); + filename = + replaceFormattedEpisodeNumber(filename, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); + filename = replaceFormattedEpisodeNumber(filename, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, + false); + filename = replace(filename, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); + filename = replace(filename, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); + filename = replace(filename, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); + filename = + replace(filename, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); + + filename = replace(filename, SerieStructureTag.TITLE, tvRelease.title); + filename = replace(filename, SerieStructureTag.QUALITY, tvRelease.quality); + filename = replace(filename, SerieStructureTag.RELEASE_GROUP, tvRelease.releaseGroup); + + filename += "." + StringUtils.substringAfterLast(tvRelease.fileNameOrName, "."); + filename = filename.removeIllegalWindowsChars(); + if (replaceSpace) { + filename = filename.replace(' ', replacingSpaceChar); + } + return filename; + } + + private String buildMovieFolderStructure(MovieRelease movieRelease) { + String filename = structure; + // order is important! + filename = replace(filename, MovieStructureTag.MOVIE_TITLE, getShowName(movieRelease.name)); + filename = replace(filename, MovieStructureTag.YEAR, formatNumber(movieRelease.year, false)); + filename = replace(filename, MovieStructureTag.QUALITY, movieRelease.quality); + filename = replace(filename, MovieStructureTag.RELEASE_GROUP, movieRelease.releaseGroup); + + filename += "." + StringUtils.substringAfterLast(movieRelease.fileNameOrName, "."); + + filename = filename.removeIllegalWindowsChars(); + if (replaceSpace) { + filename = filename.replace(' ', replacingSpaceChar); } + return filename; } - public String buildSubtitle(Release release, Subtitle sub, String filename, @Nullable Integer version) { - return buildSubtitle(release, filename, sub.language, version); + public String buildSubtitle(ReleaseWithPath release, Subtitle sub, String filename, @Nullable Integer version) { + return buildSubtitle(release.fileName, filename, sub.language, version); } - public String buildSubtitle(Release release, String filename, Language language, @Nullable Integer version) { - String extension = "." + StringUtils.substringAfterLast(release.fileName, "."); + public String buildSubtitle(String name, String filename, @Nullable Language language, + @Nullable Integer version) { + String extension = "." + StringUtils.substringAfterLast(name, "."); String subFileName = filename; if (version != null) { subFileName = subFileName.substring(0, subFileName.indexOf(extension)) + "-v$version." + - StringUtils.substringAfterLast(release.fileName, "."); + StringUtils.substringAfterLast(name, "."); } if (includeLanguageCode) { String langCode = language == null ? "" : languageTags.getOrDefault(language, language.iso639_3); - subFileName = changeExtension(subFileName, !"".equals(langCode) ? ".$langCode.srt" : ".srt"); + subFileName = changeExtension(subFileName, !langCode.isEmpty() ? ".$langCode.srt" : ".srt"); } else { subFileName = changeExtension(subFileName, ".srt"); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java index 510b51d5..4c979875 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryActionType.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.lib.library; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum LibraryActionType { NOTHING("PreferenceDialog.Action.Nothing"), RENAME("PreferenceDialog.Action.Rename"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java index 79e8c7e0..9c1b07e5 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryBuilder.java @@ -5,13 +5,16 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.settings.model.structure.StructureTag; import org.lodder.subtools.sublibrary.data.tvdb.TvdbAdapter; import org.lodder.subtools.sublibrary.data.tvdb.model.TvdbSerie; import org.lodder.subtools.sublibrary.model.ProviderIds; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; +@NullMarked public abstract sealed class LibraryBuilder permits FilenameLibraryBuilder, PathLibraryBuilder { private final @Nullable TvdbAdapter tvdbAdapter; @@ -20,7 +23,15 @@ public LibraryBuilder(@Nullable TvdbAdapter tvdbAdapter) { this.tvdbAdapter = tvdbAdapter; } - public abstract Path build(Release release); + /** + * Builds a relative or absolute Path object based on a release object. + * + * @param release The ReleaseWithPath object. + * @return The created path + */ + public abstract Path buildPath(ReleaseWithPath release); + + public abstract String buildPathStructure(Release release); protected String getShowName(String name) { return tvdbAdapter != null ? diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java index 4bacc624..a0c6cc67 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/LibraryOtherFileActionType.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.lib.library; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum LibraryOtherFileActionType { NOTHING("PreferenceDialog.Action.Nothing"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java index 8b23b848..47bf5ac4 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/lib/library/PathLibraryBuilder.java @@ -1,8 +1,8 @@ package org.lodder.subtools.multisubdownloader.lib.library; import java.nio.file.Path; -import java.nio.file.Paths; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.settings.model.LibrarySettings; import org.lodder.subtools.multisubdownloader.settings.model.structure.FolderStructureTag; @@ -11,10 +11,14 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.data.tvdb.TvdbAdapter; import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithPath; import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; +@NullMarked public final class PathLibraryBuilder extends LibraryBuilder { private final String structure; @@ -44,51 +48,68 @@ public static PathLibraryBuilder fromSettings(LibrarySettings libSettings, Manag move:libSettings.hasAnyLibraryAction(LibraryActionType.MOVE, LibraryActionType.MOVE_AND_RENAME)); } + /** + * Builds an absolute Path object based on a release object. + * + * @param release The ReleaseWithPath object. + * @return The created absolute path + */ @Override - public Path build(Release release) { + public Path buildPath(ReleaseWithPath release) { if (move) { - Path subpath = switch (release) { - case TvRelease tvRelease -> buildEpisode(tvRelease); - case MovieRelease movieRelease -> buildMovie(movieRelease); + String pathStructure = switch (release) { + case TvReleaseWithPath tvRelease -> buildEpisodeFolderStructure(tvRelease); + case MovieReleaseWithPath movieRelease -> buildMovieFolderStructure(movieRelease); }; - return libraryFolder.resolve(subpath); + return libraryFolder.resolve(pathStructure.split(FolderStructureTag.SEPARATOR.label)); } else { - return release.getPath(); + return release.path; } } - private Path buildEpisode(TvRelease tvRelease) { - String folder = structure; + @Override + public String buildPathStructure(Release release) { + if (move) { + return switch (release) { + case TvRelease tvRelease -> buildEpisodeFolderStructure(tvRelease); + case MovieRelease movieRelease -> buildMovieFolderStructure(movieRelease); + }; + } else { + return release.fileNameOrName; + } + } + + private String buildEpisodeFolderStructure(TvRelease tvRelease) { + String structure = this.structure; - folder = folder.replace(SerieStructureTag.SHOW_NAME.label, getShowName(tvRelease.name)) + structure = structure.replace(SerieStructureTag.SHOW_NAME.label, getShowName(tvRelease.name)) .removeIllegalWindowsChars(); // order is important! - folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); - folder = replaceFormattedEpisodeNumber(folder, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, false); - folder = replace(folder, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); - folder = replace(folder, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); - folder = replace(folder, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); - folder = replace(folder, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); - folder = replace(folder, SerieStructureTag.TITLE, tvRelease.title); - folder = replace(folder, SerieStructureTag.QUALITY, tvRelease.quality); - folder = replace(folder, SerieStructureTag.RELEASE_GROUP, tvRelease.releaseGroup); + structure = replaceFormattedEpisodeNumber(structure, SerieStructureTag.EPISODES_LONG, tvRelease.episodes, true); + structure = + replaceFormattedEpisodeNumber(structure, SerieStructureTag.EPISODES_SHORT, tvRelease.episodes, false); + structure = replace(structure, SerieStructureTag.SEASON_LONG, formatNumber(tvRelease.season, true)); + structure = replace(structure, SerieStructureTag.SEASON_SHORT, formatNumber(tvRelease.season, false)); + structure = replace(structure, SerieStructureTag.EPISODE_LONG, formatNumber(tvRelease.firstEpisode, true)); + structure = replace(structure, SerieStructureTag.EPISODE_SHORT, formatNumber(tvRelease.firstEpisode, false)); + structure = replace(structure, SerieStructureTag.TITLE, tvRelease.title); + structure = replace(structure, SerieStructureTag.QUALITY, tvRelease.quality); + structure = replace(structure, SerieStructureTag.RELEASE_GROUP, tvRelease.releaseGroup); if (replaceSpace) { - folder = folder.replace(' ', replacingSpaceChar); + structure = structure.replace(' ', replacingSpaceChar); } - folder = folder.trim(); - return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.label)); + return structure.trim(); } - private Path buildMovie(MovieRelease movieRelease) { - String folder = structure; + private String buildMovieFolderStructure(MovieRelease movieRelease) { + String structure = this.structure; - folder = replace(folder, MovieStructureTag.MOVIE_TITLE, movieRelease.name.removeIllegalWindowsChars()); - folder = replace(folder, MovieStructureTag.YEAR, Integer.toString(movieRelease.year)); - folder = replace(folder, MovieStructureTag.QUALITY, movieRelease.quality); + structure = replace(structure, MovieStructureTag.MOVIE_TITLE, movieRelease.name.removeIllegalWindowsChars()); + structure = replace(structure, MovieStructureTag.YEAR, Integer.toString(movieRelease.year)); + structure = replace(structure, MovieStructureTag.QUALITY, movieRelease.quality); if (replaceSpace) { - folder = folder.replace(' ', replacingSpaceChar); + structure = structure.replace(' ', replacingSpaceChar); } - folder = folder.trim(); - return Paths.get("", folder.split(FolderStructureTag.SEPARATOR.label)); + return structure.trim(); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/IndexingProgressListener.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/IndexingProgressListener.java index 6bf7c043..fd8d7b4c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/IndexingProgressListener.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/IndexingProgressListener.java @@ -1,5 +1,8 @@ package org.lodder.subtools.multisubdownloader.listeners; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface IndexingProgressListener extends StatusListener { void progress(int progress); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/SearchProgressListener.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/SearchProgressListener.java index a1abce6b..2d9b49d0 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/SearchProgressListener.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/SearchProgressListener.java @@ -1,12 +1,16 @@ package org.lodder.subtools.multisubdownloader.listeners; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.model.Release; +@NullMarked public interface SearchProgressListener extends StatusListener { void progress(SubtitleProvider provider, int jobsLeft, Release release); + void done(SubtitleProvider provider); + void progress(int progress); void completed(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/StatusListener.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/StatusListener.java index 00a1ed3e..0b7f28c3 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/StatusListener.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/listeners/StatusListener.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.listeners; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.actions.ActionException; +@NullMarked public interface StatusListener { void onError(ActionException exception); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java index bda3db30..6e2c9e31 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/Addic7edServiceProvider.java @@ -3,6 +3,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.cli.CliOption; import org.lodder.subtools.multisubdownloader.framework.Container; @@ -15,6 +16,7 @@ import org.lodder.subtools.sublibrary.Credentials; import org.lodder.subtools.sublibrary.Manager; +@NullMarked public class Addic7edServiceProvider implements ServiceProvider { protected Container app; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java index bcb5e48c..d384267a 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/LocalServiceProvider.java @@ -2,6 +2,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -9,6 +10,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.multisubdownloader.subtitleproviders.local.Local; +@NullMarked public class LocalServiceProvider implements ServiceProvider { private UserInteractionHandler userInteractionHandler; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java index 6700a103..c0b3b7f7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/OpenSubtitlesServiceProvider.java @@ -3,6 +3,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; @@ -12,6 +13,7 @@ import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.OpenSubAdapter; import org.lodder.subtools.sublibrary.Credentials; +@NullMarked public class OpenSubtitlesServiceProvider implements ServiceProvider { protected Container app; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java index ad416ef2..148300b0 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/PodnapisiServiceProvider.java @@ -2,11 +2,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.PodnapisiAdapter; +@NullMarked public class PodnapisiServiceProvider implements ServiceProvider { /* We define a priority lower than SubtitleServiceProvider */ @val @override int priority = 1; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubdlServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubdlServiceProvider.java index aca69a64..2a041034 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubdlServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubdlServiceProvider.java @@ -2,11 +2,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.subdl.SubdlAdapter; +@NullMarked public class SubdlServiceProvider implements ServiceProvider { /* We define a priority lower than SubtitleServiceProvider */ diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java index fa62adaa..bd95de11 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubsceneServiceProvider.java @@ -2,12 +2,14 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.SubsceneAdapter; +@NullMarked public class SubsceneServiceProvider implements ServiceProvider { protected Container app; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java index 68ae2a7d..bbf412bd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/SubtitleServiceProvider.java @@ -2,12 +2,14 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProviderStore; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; +@NullMarked public class SubtitleServiceProvider implements ServiceProvider { @val @override int priority = 0; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java index 83ae4c7d..fe6f91f3 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/serviceproviders/TvSubtitlesServiceProvider.java @@ -2,11 +2,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.framework.Container; import org.lodder.subtools.multisubdownloader.framework.service.providers.ServiceProvider; import org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.TvSubtitlesAdapter; +@NullMarked public class TvSubtitlesServiceProvider implements ServiceProvider { /* We define a priority lower than SubtitleServiceProvider */ diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java index b312a507..5d6ba927 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingValue.java @@ -13,6 +13,7 @@ import com.google.common.base.Objects; import extensions.java.nio.file.Path.PathExt; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.gui.extra.MemoryFolderChooser; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; @@ -27,6 +28,7 @@ import org.lodder.subtools.sublibrary.control.VideoPatterns; import org.lodder.subtools.sublibrary.util.function.TriConsumer; +@NullMarked public enum SettingValue { // SETTINGS @@ -432,9 +434,11 @@ private static > Mapper enumMapper(Class type) { return new Mapper<>(Enum::name, v -> Enum.valueOf(type, v)); } + @NullMarked private record Mapper(Function toStringMapper, Function toObjectMapper) { } + @NullMarked private interface Mappers { Mapper STRING = new Mapper<>(Function.identity(), Function.identity()); Mapper CHAR = new Mapper<>(String::valueOf, s -> s.charAt(0)); @@ -529,6 +533,7 @@ private static SettingMapTyped createSetting( return new SettingMapTyped<>(rootElementFunction, mapGetter, keyMapper, valueMapper); } + @NullMarked private static class SettingTyped extends SettingCommon { // SINGLE VALUE @@ -596,6 +601,7 @@ private static class SettingTyped extends SettingCommon { } } + @NullMarked private static class SettingMapTyped extends SettingCommon { SettingMapTyped(Function rootElementFunction, Function> mapGetter, diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java index 2f66a50f..19d22333 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/SettingsControl.java @@ -10,12 +10,14 @@ import java.util.prefs.Preferences; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.settings.model.Settings; import org.lodder.subtools.multisubdownloader.settings.model.State; import org.lodder.subtools.sublibrary.Manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class SettingsControl { private static final Logger LOGGER = LoggerFactory.getLogger(SettingsControl.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java index 6544bae9..d8bea2be 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/LibrarySettings.java @@ -5,10 +5,12 @@ import java.util.Map; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.library.LibraryActionType; import org.lodder.subtools.multisubdownloader.lib.library.LibraryOtherFileActionType; import org.lodder.subtools.sublibrary.Language; +@NullMarked public class LibrarySettings { @var String filenameStructure = ""; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java index e1d831a8..82e3fa4b 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathMatchType.java @@ -3,7 +3,9 @@ import java.awt.*; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum PathMatchType { FOLDER("/folder.png"), REGEX("/regex.gif"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java index 474772fc..305539fc 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/PathOrRegex.java @@ -1,5 +1,7 @@ package org.lodder.subtools.multisubdownloader.settings.model; +import static java.util.Objects.*; + import java.awt.*; import java.io.Serial; import java.io.Serializable; @@ -11,8 +13,9 @@ import java.util.regex.Pattern; import manifold.ext.props.rt.api.val; -import manifold.ext.rt.api.Self; +import org.jspecify.annotations.NullMarked; +@NullMarked public class PathOrRegex implements Serializable { @Serial private static final long serialVersionUID = 1L; @@ -61,12 +64,15 @@ public String toString() { } @Override - public int hashCode() { - return Objects.hash(value); + public boolean equals(Object o) { + return this == o || (o instanceof PathOrRegex that + && Objects.equals(value, that.value) + && Objects.equals(image, that.image) + && Objects.equals(isExcludedPathPredicate, that.isExcludedPathPredicate)); } @Override - public boolean equals(@Self Object obj) { - return obj instanceof PathOrRegex other && Objects.equals(value, other.value); + public int hashCode() { + return hash(value, image, isExcludedPathPredicate); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java index fbb676f7..9da3c30e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/ScreenSettings.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class ScreenSettings { @var boolean hideEpisode; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java index a6160079..9067406e 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/Settings.java @@ -12,12 +12,14 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.control.VideoPatterns; import org.lodder.subtools.sublibrary.data.UserInteractionSettingsIntf; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class Settings implements UserInteractionSettingsIntf { @var Path lastOutputDir; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsProcessEpisodeSource.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsProcessEpisodeSource.java index 529f67f6..87e57676 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsProcessEpisodeSource.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/SettingsProcessEpisodeSource.java @@ -1,6 +1,9 @@ package org.lodder.subtools.multisubdownloader.settings.model; +import org.jspecify.annotations.NullMarked; + @Deprecated//(forRemoval = true) +@NullMarked public enum SettingsProcessEpisodeSource { TVDB } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java index 1cffb265..14b8a7f8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/State.java @@ -3,7 +3,9 @@ import java.time.LocalDate; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class State { @var LocalDate latestUpdateCheck; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java index 17880356..0c7bf43f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateCheckPeriod.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.settings.model; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum UpdateCheckPeriod { MANUAL("InputPanel.UpdateInterval.Manual"), DAILY("InputPanel.UpdateInterval.Daily"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java index 42c2ece9..1324f20e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/UpdateType.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.settings.model; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum UpdateType { STABLE("InputPanel.UpdateType.Stable"), NIGHTLY("InputPanel.UpdateType.Nightly"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java index 3eb0e8c2..1b1c4fcd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/FolderStructureTag.java @@ -2,8 +2,10 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum FolderStructureTag implements StructureTag { SEPARATOR("%SEPARATOR%", Messages.getText("StructureBuilderDialog.SystemDependentSeparator")); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java index bb628a45..6cf760ce 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/MovieStructureTag.java @@ -2,8 +2,10 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum MovieStructureTag implements StructureTag { MOVIE_TITLE("%MOVIE TITLE%", "StructureBuilderDialog.MovieName"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java index 716a0224..4108df6b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/SerieStructureTag.java @@ -2,8 +2,10 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum SerieStructureTag implements StructureTag { SHOW_NAME("%SHOW NAME%", "StructureBuilderDialog.NameTvShow"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java index 811aac95..34404d4c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/settings/model/structure/StructureTag.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.settings.model.structure; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public interface StructureTag { @val String label; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/FileHasher.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/FileHasher.java index bcfeb7e1..34f2f3de 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/FileHasher.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/FileHasher.java @@ -12,71 +12,68 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import org.jspecify.annotations.NullMarked; + /** * Hash code is based on Media Player Classic. In natural language it calculates: size + 64bit checksum of the first and * last 64k (even if they overlap because the file is smaller than 128k). */ +@NullMarked public class FileHasher { /** * Size of the chunks that will be hashed in bytes (64 KB) */ - private static final int HASH_CHUNK_SIZE = 64 * 1024; + private static final int CHUNK_SIZE = 64 * 1024; public static String computeHash(Path path) throws IOException { long size = Files.size(path); - long chunkSizeForFile = Math.min(HASH_CHUNK_SIZE, size); + long chunkSize = Math.min(CHUNK_SIZE, size); try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) { - long head = computeHashForChunk(fileChannel.map(MapMode.READ_ONLY, 0, chunkSizeForFile)); - long tail = computeHashForChunk( - fileChannel.map(MapMode.READ_ONLY, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile)); + long head = hash(fileChannel.map(MapMode.READ_ONLY, 0, chunkSize)); + long tail = hash( + fileChannel.map(MapMode.READ_ONLY, Math.max(size - CHUNK_SIZE, 0), chunkSize)); return "%016x".formatted(size + head + tail); } } - public static String computeHash(InputStream stream, long length) throws IOException { + public static String computeHash(InputStream in, long length) throws IOException { - int chunkSizeForFile = (int) Math.min(HASH_CHUNK_SIZE, length); + int chunkSize = (int) Math.min(CHUNK_SIZE, length); // buffer that will contain the head and the tail chunk, chunks will overlap if length is smaller than two // chunks - byte[] chunkBytes = new byte[(int) Math.min(2 * HASH_CHUNK_SIZE, length)]; + byte[] buffer = new byte[(int) Math.min(2 * CHUNK_SIZE, length)]; - try (DataInputStream in = new DataInputStream(stream)) { + try (DataInputStream data = new DataInputStream(in)) { // first chunk - in.readFully(chunkBytes, 0, chunkSizeForFile); - - long position = chunkSizeForFile; - long tailChunkPosition = length - chunkSizeForFile; + data.readFully(buffer, 0, chunkSize); + long position = chunkSize; + long tailChunkPosition = length - chunkSize; // seek to position of the tail chunk, or not at all if length is smaller than two chunks - while (position < tailChunkPosition && (position += in.skip(tailChunkPosition - position)) >= 0) { - + while (position < tailChunkPosition && (position += data.skip(tailChunkPosition - position)) >= 0) { } + //data.skipNBytes(Math.max(0, length - 2L * chunkSize)); // second chunk, or the rest of the data if length is smaller than two chunks - in.readFully(chunkBytes, chunkSizeForFile, chunkBytes.length - chunkSizeForFile); + data.readFully(buffer, chunkSize, buffer.length - chunkSize); - long head = computeHashForChunk(ByteBuffer.wrap(chunkBytes, 0, chunkSizeForFile)); - long tail = computeHashForChunk( - ByteBuffer.wrap(chunkBytes, chunkBytes.length - chunkSizeForFile, chunkSizeForFile)); + long head = hash(ByteBuffer.wrap(buffer, 0, chunkSize)); + long tail = hash(ByteBuffer.wrap(buffer, buffer.length - chunkSize, chunkSize)); return "%016x".formatted(length + head + tail); } } - private static long computeHashForChunk(ByteBuffer buffer) { - + private static long hash(ByteBuffer buffer) { LongBuffer longBuffer = buffer.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer(); long hash = 0; - while (longBuffer.hasRemaining()) { hash += longBuffer.get(); } - return hash; } - } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleAdapter.java index 460a29f3..f5b3dd1c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleAdapter.java @@ -22,6 +22,7 @@ import name.falgout.jeffrey.throwing.ThrowingSupplier; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.function.TriFunction; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.sublibrary.Language; @@ -32,6 +33,7 @@ import org.lodder.subtools.sublibrary.data.AdapterIntf; import org.lodder.subtools.sublibrary.data.ProviderId; import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; import org.lodder.subtools.sublibrary.model.ProviderIds; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; @@ -48,13 +50,15 @@ * @param type of the serie provider id * @param type of the exception thrown by the api */ -public abstract class SubtitleAdapter implements - SubtitleProvider, AdapterIntf { +@NullMarked +public abstract class SubtitleAdapter + implements SubtitleProvider, AdapterIntf { Logger LOGGER = LoggerFactory.getLogger(SubtitleAdapter.class); @val @override Manager manager; @val UserInteractionHandler userInteractionHandler; @val(Abstract) boolean useSeasonForSerieId; + //@val @override String provider = subtitleProviderFrontEnd.name(); protected SubtitleAdapter(Manager manager, UserInteractionHandler userInteractionHandler) { this.manager = manager; @@ -70,6 +74,7 @@ public String getProvider() { // MOVIE \\ // ===== \\ + @Override public Set searchSubtitles(MovieRelease movieRelease, Language language) { Set subtitles = new HashSet<>(); try { @@ -88,8 +93,8 @@ public Set searchSubtitles(MovieRelease movieRelease, Language language) { } } if (subtitles.isEmpty()) { - if (StringUtils.isNotBlank(movieRelease.fileName)) { - Path file = movieRelease.getPath().resolve(movieRelease.fileName); + if (movieRelease instanceof MovieReleaseWithPath release) { + Path file = release.path.resolve(release.fileName); if (file.exists()) { try { subtitles.addAll(searchMovieSubtitlesWithHash(FileHasher.computeHash(file), language)); @@ -100,6 +105,7 @@ public Set searchSubtitles(MovieRelease movieRelease, Language language) { .formatted(movieRelease.name, e.getMessage()), e); } } + } } return subtitles.stream().map(subtitle -> convertToSubtitle(movieRelease, subtitle)).toSet(); @@ -117,6 +123,7 @@ public abstract Collection searchMovieSubtitlesWithName(String name, @N // SERIE \\ // ===== \\ + @Override public Set searchSubtitles(TvRelease tvRelease, Language language) { // Search using other provider ids List subtitles = tvRelease.episodes.stream().flatMap(episode -> { @@ -151,7 +158,9 @@ public Set searchSubtitles(TvRelease tvRelease, Language language) { } catch (Exception e) { String displayName = StringUtils.defaultIfBlank(tvRelease.originalName, tvRelease.name); LOGGER.error("API %s searchSubtitles for serie [%s] (%s)".formatted(source.name, - TvRelease.formatName(displayName, tvRelease.season, tvRelease.firstEpisode), e.getMessage()), e); + TvRelease.formatName(displayName, tvRelease.season, tvRelease.firstEpisode), + e.getMessage()), + e); return Set.of(); } } @@ -162,6 +171,7 @@ public abstract Collection searchSubtitles(ProviderIds providerIds, int public abstract Collection searchSubtitles(SerieMapping serieMapping, int season, int episode, Language language) throws X; + @Override public Optional getProviderSerieMapping(TvRelease tvRelease) throws X { if (StringUtils.isNotBlank(tvRelease.customName)) { return getProviderSerieMapping(tvRelease, tvRelease.originalName, tvRelease.customName); @@ -191,7 +201,8 @@ private Optional getProviderSerieMapping(TvRelease tvRelease, Stri * repeated user prompts during the same execution. *

*

- * If {@code nameToSearchFor} differs from {@code name}, it indicates that the user has entered a custom search name. + * If {@code nameToSearchFor} differs from {@code name}, it indicates that the user has entered a custom search + * name. * This distinction is used to determine caching behavior and result matching logic. *

* @@ -273,7 +284,8 @@ private Optional getProviderSerieMapping(String name, String nameT * repeated user prompts during the same execution. *

*

- * If {@code nameToSearchFor} differs from {@code name}, it indicates that the user has entered a custom search name. + * If {@code nameToSearchFor} differs from {@code name}, it indicates that the user has entered a custom search + * name. * This distinction is used to determine caching behavior and result matching logic. *

* @@ -373,8 +385,8 @@ public Optional getProviderR // expiration time. cacheKey.store( value:Value.ofOptional(Optional.empty()), - timeToLive:1 day, - storeTempNullValue:true); + storeTempNullValue:true, + timeToLive:1 day); return Optional.empty(); } else { Optional result = Optional.of(releaseMapping); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java index 0e2e2737..104be1b7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleApi.java @@ -1,17 +1,16 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders; +import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.data.ApiIntf; import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public interface SubtitleApi extends ApiIntf { @val SubtitleProviderFrontEnd subtitleProviderFrontEnd; @val SubtitleSource source = subtitleProviderFrontEnd.subtitleSource; - - @Override - default String getProvider() { - return subtitleProviderFrontEnd.name; - } + @val @override String provider = subtitleProviderFrontEnd.name; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java index 91ac5003..951d5b3c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProvider.java @@ -4,6 +4,7 @@ import java.util.Set; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.cache.CacheType; @@ -16,6 +17,7 @@ import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.slf4j.LoggerFactory; +@NullMarked public interface SubtitleProvider { @val Manager manager; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProviderStore.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProviderStore.java index 7565269d..2b83cb1b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProviderStore.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/SubtitleProviderStore.java @@ -3,14 +3,15 @@ import java.util.HashSet; import java.util.Set; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SubtitleProviderStore { protected final Set> subtitleProviders = new HashSet<>(); - public Set> getAllProviders() { - return new HashSet<>(this.subtitleProviders); - } + @val Set> allProviders = Set.copyOf(this.subtitleProviders); public void addProvider(SubtitleProvider provider) { this.subtitleProviders.add(provider); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/Addic7edAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/Addic7edAdapter.java index 319350e9..e35023dd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/Addic7edAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/Addic7edAdapter.java @@ -9,6 +9,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -25,6 +26,7 @@ import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; +@NullMarked public final class Addic7edAdapter extends SubtitleAdapter { @@ -32,7 +34,7 @@ public final class Addic7edAdapter extends SubtitleAdapter download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); ThrowingConsumer validateFunction = content -> { if (content.contains("Daily Download count exceeded")) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownAdapter.java index 96e8c626..2bf31b1c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownAdapter.java @@ -9,6 +9,7 @@ import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; import org.gestdown.model.ShowDto; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -26,8 +27,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public final class Addic7edProxyGestdownAdapter extends - SubtitleAdapter { + SubtitleAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(Addic7edProxyGestdownAdapter.class); @@ -101,7 +104,8 @@ public Collection searchSubtitles(ProviderIds pro @Override public Collection searchSubtitles(SerieMapping serieMapping, int season, int episode, Language language) throws Addic7edException { - LOGGER.debug("$provider - getSubtitles: {}", TvRelease.formatName(serieMapping.providerName, season, episode)); + LOGGER.debug("$provider - getSubtitles: {}", + TvRelease.formatName(serieMapping.providerName, season, episode)); return api.getSubtitles(serieMapping.providerId, season, episode, language); } @@ -115,6 +119,7 @@ public Addic7edProxyGestdownSubtitle convertToSubtitle(Release release, Addic7ed } + @NullMarked private enum ReturnCode { NOT_FOUND((code, _) -> code == HttpStatus.NOT_FOUND), RATE_LIMIT_REACHED((code, _) -> code == HttpStatus.TOO_MANY_REQUESTS), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownApi.java index f7ca1202..1c52d69e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/Addic7edProxyGestdownApi.java @@ -126,7 +126,6 @@ private Addic7edProxyGestdownSubtitle mapToSubtitle(SubtitleDto sub, EpisodeDto language:language, quality:extraInfoParser.getQualityKeyword(), releaseGroup:extraInfoParser.getReleaseGroupBestEffort(), - uploader:"", hearingImpaired:false); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSerieId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSerieId.java index 7d17d300..afeb409c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSerieId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSerieId.java @@ -1,13 +1,17 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.addic7ed.proxy.gestdown.model; +import static util.Utils.*; + import java.io.Serial; import java.util.OptionalInt; import java.util.UUID; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.ProviderId; +@NullMarked public class Addic7edProxyGestdownSerieId extends ProviderId { @Serial @@ -19,7 +23,7 @@ public class Addic7edProxyGestdownSerieId extends ProviderId { public Addic7edProxyGestdownSerieId(String text, UUID id, @Nullable Integer tvdbId=null, @Nullable Integer tmdbId=null) { super(text, id.toString()); - this.tvdbId = tvdbId == null ? OptionalInt.empty() : OptionalInt.of(tvdbId); - this.tmdbId = tmdbId == null ? OptionalInt.empty() : OptionalInt.of(tmdbId); + this.tvdbId = ifNotNullOrElseGet(tvdbId, OptionalInt::of, OptionalInt::empty); + this.tmdbId = ifNotNullOrElseGet(tmdbId, OptionalInt::of, OptionalInt::empty); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSubtitle.java index 00ae0152..d77c78ce 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/addic7ed/proxy/gestdown/model/Addic7edProxyGestdownSubtitle.java @@ -6,31 +6,36 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class Addic7edProxyGestdownSubtitle extends Subtitle { private final String url; + @val @override SubtitleSource source = SubtitleSource.ADDIC7ED; public Addic7edProxyGestdownSubtitle(String url, - @Nullable String fileName=null, - @Nullable Language language=null, + String fileName, + Language language, @Nullable String releaseGroup=null, @Nullable String uploader=null, boolean hearingImpaired=false, - @Nullable String quality=null) { + String quality) { - super(fileName, language, releaseGroup, uploader, SubtitleSource.ADDIC7ED, hearingImpaired, quality); + super(fileName, language, releaseGroup, uploader, hearingImpaired, quality); this.url = url; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); manager.downloadAndExtractFile(url, subPath); return List.of(subPath); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/Local.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/Local.java index 3fb16ab5..83affc87 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/Local.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/Local.java @@ -11,6 +11,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.NotImplementedException; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.lib.control.MovieReleaseControl; import org.lodder.subtools.multisubdownloader.lib.control.TvReleaseControl; import org.lodder.subtools.multisubdownloader.settings.model.Settings; @@ -21,16 +22,18 @@ import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; import org.lodder.subtools.sublibrary.exception.ReleaseControlException; -import org.lodder.subtools.sublibrary.exception.ReleaseParseException; import org.lodder.subtools.sublibrary.model.MovieRelease; +import org.lodder.subtools.sublibrary.model.MovieReleaseWithPath; import org.lodder.subtools.sublibrary.model.ProviderIdType; import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.lodder.subtools.sublibrary.userinteraction.UserInteractionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class Local implements SubtitleProvider { private static final Logger LOGGER = LoggerFactory.getLogger(Local.class); @@ -63,7 +66,7 @@ public Set searchSubtitles(TvRelease tvRelease, Language language for (Path fileSub : getPossibleSubtitles(filter)) { try { ReleaseParser.parse(fileSub).stream() - .filterCast(TvRelease.class) + .filterCast(TvReleaseWithoutPath.class) .filter(release -> release.season == tvRelease.season) .filter(release -> release.episodes.containsAll(tvRelease.episodes)) .mapEx(tvReleaseControl::process) @@ -76,7 +79,7 @@ public Set searchSubtitles(TvRelease tvRelease, Language language .orElseGet(() -> new LocalSubtitle(fileSub, language, ReleaseParser.getQualityKeyword(fileSub.fileNameAsString), ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true))))); - } catch (ReleaseParseException | ReleaseControlException e) { + } catch (ReleaseControlException e) { LOGGER.error(e.getMessage(), LOGGER.isDebugEnabled() ? e : null); } } @@ -94,7 +97,7 @@ public Set searchSubtitles(MovieRelease movieRelease, Language la for (Path fileSub : getPossibleSubtitles(filter)) { try { ReleaseParser.parse(fileSub).stream() - .filterCast(MovieRelease.class) + .filterCast(MovieReleaseWithPath.class) .mapEx(movieCtrl::process) .filter(release -> release.hasSameId(movieRelease, ProviderIdType.IMDB)) .filter(_ -> DetectLanguage.execute(fileSub) == language) @@ -105,7 +108,7 @@ public Set searchSubtitles(MovieRelease movieRelease, Language la .orElseGet(() -> new LocalSubtitle(fileSub, language, ReleaseParser.getQualityKeyword(fileSub.fileNameAsString), ReleaseParser.extractReleaseGroup(fileSub.fileNameAsString, true))))); - } catch (ReleaseParseException | ReleaseControlException e) { + } catch (ReleaseControlException e) { LOGGER.error(e.getMessage(), LOGGER.isDebugEnabled() ? e : null); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/model/LocalSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/model/LocalSubtitle.java index dbb565ea..8ededee7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/model/LocalSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/local/model/LocalSubtitle.java @@ -6,28 +6,33 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class LocalSubtitle extends Subtitle { private final Path path; + @val @override SubtitleSource source = SubtitleSource.LOCAL; public LocalSubtitle(Path path, - @Nullable Language language=null, + Language language, @Nullable String releaseGroup=null, @Nullable String quality=null) { - super(path.fileNameAsString, language, releaseGroup, null, SubtitleSource.LOCAL, false, quality); + super(path.fileNameAsString, language, releaseGroup, null, false, quality); this.path = path; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path subPath = destinationFolder.resolve(path.fileName); path.copyToDir(subPath); return List.of(subPath); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubAdapter.java index a4bb2f7e..8e87dc4f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubAdapter.java @@ -1,12 +1,14 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles; +import static util.Utils.*; + import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Optional; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -26,7 +28,9 @@ import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import org.opensubtitles.model.Subtitle; import org.opensubtitles.model.SubtitleAttributes; +import org.opensubtitles.model.SubtitleAttributesUploader; +@NullMarked public final class OpenSubAdapter extends SubtitleAdapter { @@ -127,6 +131,7 @@ public Collection searchSubtitles(SerieMapping public OpenSubtilteSubtitle convertToSubtitle(Release release, org.opensubtitles.model.Subtitle sub) { SubtitleAttributes attr = sub.getAttributes(); Language language = Language.ofIso639_1(attr.language); + String uploader = ifNotNull(attr.getUploader(), SubtitleAttributesUploader::getName); int fileId = attr.files.stream().findFirst().orElseThrow().fileId.intValue(); return ReleaseParser.parse(attr.release) .map(r -> new OpenSubtilteSubtitle( @@ -134,7 +139,7 @@ public OpenSubtilteSubtitle convertToSubtitle(Release release, org.opensubtitles fileName:attr.release, language:language, releaseGroup:r.releaseGroup, - uploader:attr.getUploader() != null ? attr.getUploader().getName() : null, + uploader:uploader, quality:r.quality, hearingImpaired:Boolean.TRUE == attr.isHearingImpaired())) .orElseGet(() -> { @@ -142,9 +147,9 @@ public OpenSubtilteSubtitle convertToSubtitle(Release release, org.opensubtitles return new OpenSubtilteSubtitle( urlSupplier:() -> api.getDownloadUrl(fileId), fileName:attr.release, - language:Language.ofIso639_1(attr.language), + language:language, releaseGroup:extraInfo.getReleaseGroupBestEffort(), - uploader:attr.getUploader() != null ? attr.getUploader().getName() : null, + uploader:uploader, quality:extraInfo.qualityKeyword, hearingImpaired:Boolean.TRUE == attr.isHearingImpaired()); }); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java index e4bf6ea9..6eadd02b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/OpenSubtitlesApi.java @@ -126,7 +126,7 @@ private String getBearerToken(String username, String password) throws OpenSubti .get(() -> getBearerTokenWithoutCache(username, password) .orElseThrow(() -> OpenSubtitleApiException.noResult("Could not acquire a bearer token, " + "invalid username/password?")), - timeToLive:23.5 hr); + 23.5 hr); } private static Optional getBearerTokenWithoutCache(String username, String password) @@ -160,14 +160,14 @@ public List getProviderSerieIds(String serieName) throws OpenSub .getCollection(() -> { try { return manager.getAsJsonArray(new PageContentParams( - url:"https://www.opensubtitles.org/libs/suggest.php?format=json3&MovieName=" - + URLEncoder.encode(serieName.toLowerCase(), StandardCharsets.UTF_8), - cacheType:CacheType.MEMORY, - userAgent:"", - retry:new Retry( - 1, - exc -> exc instanceof HttpClientException e && e.responseCode == 429, - 5Second) + url:"https://www.opensubtitles.org/libs/suggest.php?format=json3&MovieName=" + + URLEncoder.encode(serieName.toLowerCase(), StandardCharsets.UTF_8), + cacheType:CacheType.MEMORY, + userAgent:"", + retry:new Retry( + 1, + exc -> exc instanceof HttpClientException e && e.responseCode == 429, + 5 Second) )) .streamJsonObjects() .filter(show -> "tv".equals(show.getString("kind"))) @@ -234,28 +234,31 @@ public List searchSubtitles( .add("trustedSources", trustedSources) .add("type", type) .add("userId", userId) - .add("year", year)).getCollection(() -> { - Integer imdbIdInt = StringUtils.isNotBlank(imdbId) ? Integer.parseInt(imdbId.replace("tt", "")) : null; - return apiCall( - () -> subtitlesApi.get().subtitles(id, imdbIdInt, tmdbId, getValue(type), query, - language != null ? language.iso639_1 : null, movieHash, userId, getValue(hearingImpaired), - getValue(foreignPartsOnly), getValue(trustedSources), getValue(machineTranslated), - getValue(aiTranslated), orderBy == null ? null : orderBy.paramName, getValue(orderDirection), - parentFeatureId, parentImdbId, parentTmdbId, season, episode, year, getValue(movieHashMatch), page, - USER_AGENT)).execute().getData(); - // TODO is this filtering needed? - // String name = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.name, "[^A-Za-z]", "")); - // String originalName = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.originalName, "[^A-Za-z]", "")); - // .filter(file -> { - // String subFileName = file.getFileName().replaceAll("[^A-Za-z]", "").toLowerCase(); - // return subFileName.contains(name) || - // (StringUtils.isNotBlank(originalName) && subFileName.contains(originalName)); - // }) - }); + .add("year", year)) + .getCollection(() -> { + Integer imdbIdInt = StringUtils.isNotBlank(imdbId) ? Integer.parseInt(imdbId.replace("tt", "")) : null; + return apiCall( + () -> subtitlesApi.get().subtitles(id, imdbIdInt, tmdbId, getValue(type), query, + language != null ? language.iso639_1 : null, movieHash, userId, getValue(hearingImpaired), + getValue(foreignPartsOnly), getValue(trustedSources), getValue(machineTranslated), + getValue(aiTranslated), orderBy == null ? null : orderBy.paramName, getValue(orderDirection), + parentFeatureId, parentImdbId, parentTmdbId, season, episode, year, getValue(movieHashMatch), + page, + USER_AGENT)).execute().getData(); + // TODO is this filtering needed? + // String name = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.name, "[^A-Za-z]", "")); + // String originalName = StringUtils.lowerCase(RegExUtils.replaceAll(tvRelease.originalName, + // "[^A-Za-z]", "")); + // .filter(file -> { + // String subFileName = file.getFileName().replaceAll("[^A-Za-z]", "").toLowerCase(); + // return subFileName.contains(name) || + // (StringUtils.isNotBlank(originalName) && subFileName.contains(originalName)); + // }) + }); } - public String getDownloadUrl(int fileId) throws OpenSubtitleApiException { + public @Nullable String getDownloadUrl(int fileId) throws OpenSubtitleApiException { return getCache("downloadUrl", b -> b.add("fileId", fileId)) .get(() -> { try (HttpClient client = HttpClient.newHttpClient()) { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleApiException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleApiException.java index 751bad53..80ce5107 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleApiException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleApiException.java @@ -6,11 +6,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.CacheStrategy; import org.lodder.subtools.sublibrary.LogLevel; import org.lodder.subtools.sublibrary.util.http.ApiExceptionIntf; import org.lodder.subtools.sublibrary.util.http.HttpStatus; +@NullMarked public class OpenSubtitleApiException extends OpenSubtitleException implements ApiExceptionIntf { @val @override HttpStatus errorCode; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleException.java index 1c0dd3f1..fa4a48a8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/exception/OpenSubtitleException.java @@ -4,9 +4,11 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class OpenSubtitleException extends SubtitlesProviderException { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtilteSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtilteSubtitle.java index dd6068af..50027485 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtilteSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtilteSubtitle.java @@ -6,7 +6,10 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import name.falgout.jeffrey.throwing.ThrowingSupplier; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.exception.OpenSubtitleException; import org.lodder.subtools.sublibrary.Language; @@ -14,25 +17,27 @@ import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class OpenSubtilteSubtitle extends Subtitle { private final ThrowingSupplier urlSupplier; + @val @override SubtitleSource source = SubtitleSource.OPENSUBTITLES; - public OpenSubtilteSubtitle(ThrowingSupplier urlSupplier, + public OpenSubtilteSubtitle(ThrowingSupplier<@Nullable String, OpenSubtitleException> urlSupplier, @Nullable String fileName=null, - @Nullable Language language=null, + Language language, @Nullable String releaseGroup=null, @Nullable String uploader=null, @Nullable String quality=null, boolean hearingImpaired=false) { - super(fileName, language, releaseGroup, uploader, SubtitleSource.OPENSUBTITLES, hearingImpaired, quality); + super(fileName, language, releaseGroup, uploader, hearingImpaired, quality); this.urlSupplier = urlSupplier; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { try { String url = urlSupplier.get(); Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieMetadata.java index c71a8c60..3b98e41f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesMovieMetadata.java @@ -1,10 +1,14 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.model; +import static java.util.Objects.*; + import java.util.Objects; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class OpenSubtitlesMovieMetadata { @var String name; @@ -22,14 +26,16 @@ public OpenSubtitlesMovieMetadata(String name, int imdbId) { } @Override - public boolean equals(Object object) { - return object instanceof OpenSubtitlesMovieMetadata other - && imdbId == other.imdbId && year == other.year && name.equals(other.name); + public boolean equals(Object o) { + return this == o || (o instanceof OpenSubtitlesMovieMetadata that + && year == that.year + && imdbId == that.imdbId + && Objects.equals(name, that.name)); } @Override public int hashCode() { - return Objects.hash(name, year, imdbId); + return hash(name, year, imdbId); } @Override diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleMetadata.java index 93d5011c..404ee84a 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpenSubtitlesSubtitleMetadata.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.model; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +@NullMarked public class OpenSubtitlesSubtitleMetadata { @var String userNickName; @var String subFormat; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleId.java index 69bee94a..ed8dd839 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/model/OpensubtitleId.java @@ -3,8 +3,10 @@ import java.io.Serial; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.data.ProviderId; +@NullMarked public class OpensubtitleId extends ProviderId { @Serial private static final long serialVersionUID = 1L; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java index 6718ecd0..7bf77438 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/AiTranslatedEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum AiTranslatedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java index 99fb9a4f..3ad89a10 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ForeignPartsOnlyEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum ForeignPartsOnlyEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"), ONLY("only"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java index bfee82dd..a1cae628 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/HearingImpairedEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum HearingImpairedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"), ONLY("only"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java index 1cd17c95..9bb97ffd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MachineTranslatedEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum MachineTranslatedEnum implements ParamIntf { EXCLUDE("exclude"), INCLUDE("include"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java index 4727e1b3..85c62bca 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/MoviehashMatchEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum MoviehashMatchEnum implements ParamIntf { INCLUDE("include"), ONLY("only"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java index 0bdffb2f..753c53ad 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/OrderDirectionEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum OrderDirectionEnum implements ParamIntf { ASCENDING("asc"), DESCENDING("desc"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java index 0282aaa9..40a4e9aa 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/ParamIntf.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public interface ParamIntf { @val String value; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java index 0ea56397..2a7023d7 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/SearchSubtitlesEnum.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.opensubtitles.param; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum SearchSubtitlesEnum { // exclude, include (default: exclude) diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java index f1dd606b..1b53a123 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TrustedSourcesEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum TrustedSourcesEnum implements ParamIntf { INCLUDE("include"), ONLY("only"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java index fd33112e..1a6b7be0 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/opensubtitles/param/TypeEnum.java @@ -2,7 +2,9 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum TypeEnum implements ParamIntf { MOVIE("movie"), EPISODE("episode"), ALL("all"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiAdapter.java index 33aee4a3..a5d8f79e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiAdapter.java @@ -2,11 +2,11 @@ import java.util.Collection; import java.util.List; -import java.util.Optional; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -22,6 +22,7 @@ import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; +@NullMarked public final class PodnapisiAdapter extends SubtitleAdapter { @@ -60,7 +61,7 @@ public Collection searchMovieSubtitlesWithName(String // SERIE \\ // ===== \\ - + @Override public List getSerieProviderIdById(ProviderIds providerIds, @Nullable Integer season) throws PodnapisiException { return List.of(); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiApi.java index 6053ffe4..74cc83b8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/PodnapisiApi.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.function.Function; import manifold.ext.props.rt.api.override; @@ -16,6 +17,7 @@ import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.exception.PodnapisiApiException; @@ -34,6 +36,7 @@ import org.lodder.subtools.sublibrary.util.UrlBuilder; import org.lodder.subtools.sublibrary.util.http.HttpClientException; +@NullMarked public class PodnapisiApi implements SubtitleApi { private static final String DOMAIN = "https://www.podnapisi.net"; @@ -63,17 +66,22 @@ public PodnapisiApi(Manager manager, String userAgent) { */ public List getMovieSubtitles(String title, @Nullable Integer year, Language language, ProviderIds providerIds) throws PodnapisiApiException { - return providerIds.getImdbId() - .mapConsumeEx(imdbId -> getSubtitles( - MapUtil.create( - SearchParam.IMDB, imdbId, - SearchParam.LANGUAGE, language.iso639_1, - SearchParam.YEAR, year))) - .orElseGetEx(() -> getSubtitles( - MapUtil.create( - SearchParam.KEYWORDS, URLEncoder.encode(title.trim().toLowerCase(), UTF_8), - SearchParam.LANGUAGE, language.iso639_1, - SearchParam.YEAR, year))); + Optional> podnapisiSubtitleMetadata; + try { + podnapisiSubtitleMetadata = providerIds.getImdbId() + .mapEx(imdbId -> getSubtitles( + MapUtil.create( + SearchParam.IMDB, imdbId, + SearchParam.LANGUAGE, language.iso639_1, + SearchParam.YEAR, year))); + } catch (PodnapisiApiException e) { + podnapisiSubtitleMetadata = Optional.empty(); + } + return podnapisiSubtitleMetadata.orElseGetEx(() -> getSubtitles( + MapUtil.create( + SearchParam.KEYWORDS, URLEncoder.encode(title.trim().toLowerCase(), UTF_8), + SearchParam.LANGUAGE, language.iso639_1, + SearchParam.YEAR, year))); } // ===== \\ @@ -127,11 +135,11 @@ public List getSerieSubtitlesUsingImdbId(String imdbI public List getSerieSubtitles(String serieName, int season, int episode, Language language) throws PodnapisiApiException { return getSubtitles( - MapUtil.create( - SearchParam.KEYWORDS, URLEncoder.encode(serieName.trim().toLowerCase(), UTF_8), - SearchParam.LANGUAGE, language.iso639_1, - SearchParam.SEASON, season, - SearchParam.EPISODE, episode)); + MapUtil.create( + SearchParam.KEYWORDS, URLEncoder.encode(serieName.trim().toLowerCase(), UTF_8), + SearchParam.LANGUAGE, language.iso639_1, + SearchParam.SEASON, season, + SearchParam.EPISODE, episode)); } // ====== \\ @@ -175,6 +183,7 @@ private List getSubtitles(Map download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); manager.downloadAndExtractFile(url, subPath); return List.of(subPath); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleMetadata.java index 9bd145a5..67867169 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/podnapisi/model/PodnapisiSubtitleMetadata.java @@ -2,8 +2,10 @@ import java.io.Serializable; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.Language; +@NullMarked public record PodnapisiSubtitleMetadata(String subtitleId, String name, String imdb, Language language, String uploaderName, String releaseString, String url, boolean hearingImpaired, String year, double rating, boolean isInexact=false) implements Serializable { diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlAdapter.java index e307d081..06d685b4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlAdapter.java @@ -3,10 +3,10 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Optional; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -23,6 +23,7 @@ import org.lodder.subtools.sublibrary.settings.model.SerieMapping; import subdl.Serie.ReleaseType; +@NullMarked public final class SubdlAdapter extends SubtitleAdapter { @@ -62,6 +63,7 @@ public Collection searchMovieSubtitlesWithName(String nam // SERIE \\ // ===== \\ + @Override public List getSerieProviderIdById(ProviderIds providerIds, @Nullable Integer season) throws SubdlException { return providerIds.getImdbId().flatMapEx(imdbId -> api.getProviderIdUsingImdbId(imdbId).map(List::of)) @@ -103,22 +105,22 @@ public Collection searchSubtitles(SerieMapping serieMappi public SubdlSubtitle convertToSubtitle(Release originalRelease, SubdlSubtitleMetadata sub) { return ReleaseParser.parse(sub.title).orElseMapEx(() -> ReleaseParser.parse(sub.fileName)) .map(release -> new SubdlSubtitle( - url:sub.url, - title:sub.title, - language:sub.language, - quality:release.quality, - releaseGroup:release.releaseGroup, - uploader:sub.uploader, - hearingImpaired:sub.hearingImpaired, - forRelease:originalRelease)) + sub.url, + sub.title, + sub.language, + release.releaseGroup, + sub.uploader, + sub.hearingImpaired, + release.quality, + originalRelease)) .orElseGet(() -> new SubdlSubtitle( - url:sub.url, - title:sub.title, - language:sub.language, - quality:ReleaseParser.getQualityKeyword(sub.title + " " + sub.fileName), - releaseGroup:ReleaseParser.extractReleaseGroup(sub.title, sub.title.endsWith(".zip")), - uploader:sub.uploader, - hearingImpaired:sub.hearingImpaired, - forRelease:originalRelease)); + sub.url, + sub.title, + sub.language, + ReleaseParser.extractReleaseGroup(sub.title, sub.title.endsWith(".zip")), + sub.uploader, + sub.hearingImpaired, + ReleaseParser.getQualityKeyword(sub.title + " " + sub.fileName), + originalRelease)); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlApi.java index ba5cd2bf..d346edfb 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlApi.java @@ -12,6 +12,7 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; import manifold.json.rt.api.Requester; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.subdl.exception.SubdlApiException; @@ -31,6 +32,7 @@ * An implementation of the {@link SubtitleApi} interface for interacting with the SubDL subtitle provider. * It allows fetching subtitle identifiers and subtitle metadata from the SubDL API. */ +@NullMarked public class SubdlApi implements SubtitleApi { private static final Logger LOGGER = LoggerFactory.getLogger(SubtitleApi.class); @@ -246,6 +248,7 @@ private Serie getSerie(Map paramMap) throws SubdlApiE }); } + @NullMarked private enum SearchParam { API_KEY("api_key"), SUBDL_ID("sd_id"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlLanguage.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlLanguage.java index 4c0ef76a..b65213a9 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlLanguage.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/SubdlLanguage.java @@ -4,8 +4,10 @@ import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.Language; +@NullMarked public enum SubdlLanguage { ARABIC(Language.ARABIC, "AR"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlApiException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlApiException.java index 9369e9af..8d2defb3 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlApiException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlApiException.java @@ -6,11 +6,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.CacheStrategy; import org.lodder.subtools.sublibrary.LogLevel; import org.lodder.subtools.sublibrary.util.http.ApiExceptionIntf; import org.lodder.subtools.sublibrary.util.http.HttpStatus; +@NullMarked public class SubdlApiException extends SubdlException implements ApiExceptionIntf { @val @override HttpStatus errorCode; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlException.java index eab701ad..15309910 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/exception/SubdlException.java @@ -4,9 +4,11 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class SubdlException extends SubtitlesProviderException { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSerieId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSerieId.java index 91ba1bd4..4878d7c4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSerieId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSerieId.java @@ -3,11 +3,13 @@ import java.io.Serial; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.ProviderId; import subdl.Serie; import subdl.Serie.ReleaseType; +@NullMarked public class SubdlSerieId extends ProviderId { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitle.java index b60e829b..4bae4f2d 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitle.java @@ -6,47 +6,45 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; -import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Stream; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.control.ReleaseParser; -import org.lodder.subtools.sublibrary.exception.ReleaseParseException; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class SubdlSubtitle extends Subtitle { private static final Logger LOGGER = LoggerFactory.getLogger(SubdlSubtitle.class); private final String url; private final Release forRelease; + @val @override SubtitleSource source = SubtitleSource.SUBDL; - public SubdlSubtitle(String url, - @Nullable String title=null, - @Nullable Language language=null, - @Nullable String releaseGroup=null, - @Nullable String uploader=null, - boolean hearingImpaired=false, - @Nullable String quality=null, - Release forRelease) { + public SubdlSubtitle(String url, String title, Language language, @Nullable String releaseGroup, String uploader, + boolean hearingImpaired, @Nullable String quality, Release forRelease) { - super(title, language, releaseGroup, uploader, SubtitleSource.SUBDL, hearingImpaired, quality); + super(title, language, releaseGroup, uploader, hearingImpaired, quality); this.url = url; this.forRelease = forRelease; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("multisubdownloader").resolve("subdl"); Files.createDirectories(tempDir); String zipFileName = url.contains("/") ? StringUtils.substringAfterLast(url, "/").removeIllegalWindowsChars() : @@ -59,49 +57,47 @@ public List download(Manager manager, Path destinationFolder, // find all extracted subtitle files and move them to the destination folder, renaming them using the // provided function - boolean multipleDirectories = Files.list(unzipPath).filter(Files::isDirectory).count() > 1; - try (Stream stream = Files.walk(unzipPath)) { - List subtitlesToCopy = stream - .filter(Files::isRegularFile) - .filter(path -> path.toString().toLowerCase().endsWith(".srt")) - .map(path -> { - if (multipleDirectories && forRelease instanceof TvRelease tvRelease) { - try { - Boolean matchingSub = - ReleaseParser.parse(path).filter(TvRelease.class::isInstance).map(TvRelease.class::cast) - .map(release -> release.season == tvRelease.season && - release.episodes.stream().anyMatch(tvRelease.episodes::contains)) - .orElse(false); + try (Stream files = Files.list(unzipPath)) { + boolean multipleDirectories = files.filter(Files::isDirectory).count() > 1; + try (Stream stream = Files.walk(unzipPath)) { + List subtitlesToCopy = stream + .filter(Files::isRegularFile) + .filter(path -> path.toString().toLowerCase().endsWith(".srt")) + .mapFilterNonNull(path -> { + if (multipleDirectories && forRelease instanceof TvReleaseWithoutPath tvRelease) { + Boolean matchingSub = ReleaseParser.parse(path).filter(TvReleaseWithPath.class::isInstance) + .map(TvReleaseWithPath.class::cast) + .map(release -> release.season == tvRelease.season && + release.episodes.stream().anyMatch(tvRelease.episodes::contains)) + .orElse(false); if (!matchingSub) { return null; } - } catch (ReleaseParseException e) { - LOGGER.warn("Could not parse subtitle file $path: " + e.getMessage()); } - } - return path; - }) - .filter(Objects::nonNull) - .toList(); + return path; + }) + .toList(); - AtomicInteger counter = subtitlesToCopy.size() > 1 ? new AtomicInteger(0) : null; - return subtitlesToCopy.stream().map(path -> { - Path destination = destinationFolder.resolve(fileNameFunction.apply(counter)); - while (Files.exists(destination)) { - Path destinationNew = destinationFolder.resolve(fileNameFunction.apply(counter)); - if (destinationNew.equals(destination)) { - LOGGER.warn("Could not copy subtitle $path to $destinationNew, because it already exists"); + AtomicInteger counter = subtitlesToCopy.size() > 1 ? new AtomicInteger(0) : null; + return subtitlesToCopy.stream().mapFilterNonNull(path -> { + Path destination = destinationFolder.resolve(fileNameFunction.apply(counter)); + while (Files.exists(destination)) { + Path destinationNew = destinationFolder.resolve(fileNameFunction.apply(counter)); + if (destinationNew.equals(destination)) { + LOGGER.warn("Could not copy subtitle $path to $destinationNew, because it already " + + "exists"); + return null; + } + } + try { + return Files.copy(path, destination, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + LOGGER.error("Could not copy subtitle file $path to $destination: " + e.getMessage()); return null; } - } - try { - return Files.copy(path, destination, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - LOGGER.error("Could not copy subtitle file $path to $destination: " + e.getMessage()); - return null; - } - }).filter(Objects::nonNull) - .toList(); + }) + .toList(); + } } } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitleMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitleMetadata.java index 29d8f6c0..8246f95a 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitleMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subdl/model/SubdlSubtitleMetadata.java @@ -3,6 +3,7 @@ import java.io.Serializable; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.Language; /** @@ -17,7 +18,8 @@ * @param uploader The name of the person or group that uploaded the subtitle. * @param hearingImpaired Indicates whether the subtitle includes hearing-impaired annotations. */ +@NullMarked public record SubdlSubtitleMetadata(String title, String fileName, String url, int season, List episodes, - String uploader, boolean hearingImpaired, Language language) + String uploader, boolean hearingImpaired, Language language) implements Serializable { } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneAdapter.java index f2a4a4dd..73f6119f 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneAdapter.java @@ -1,5 +1,6 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene; +import static java.util.Objects.*; import static org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model.SearchResultType.*; import java.util.Collection; @@ -8,11 +9,11 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.Optional; import java.util.function.ToIntFunction; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -29,6 +30,7 @@ import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; +@NullMarked public final class SubsceneAdapter extends SubtitleAdapter { @@ -73,8 +75,8 @@ public Collection searchMovieSubtitlesWithName(String @Override public List getSerieProviderIdById(ProviderIds providerIds, Integer season) throws SubsceneException { - return providerIds.getImdbId().mapEx(imdbId -> getSortedSerieProviderIds(imdbId, - Objects.requireNonNull(season))).orElseGet(List::of); + return providerIds.getImdbId().mapEx(imdbId -> getSortedSerieProviderIds(imdbId, requireNonNull(season))) + .orElseGet(List::of); } /** @@ -86,7 +88,7 @@ public List getSerieProviderIdById(ProviderIds providerIds, Int @Override public List getSortedSerieProviderIds(String searchQuery, Integer season) throws SubsceneException { - ToIntFunction providerTypeFunction = value -> switch (value) { + ToIntFunction<@Nullable SearchResultType> providerTypeFunction = value -> switch (value) { case EXACT -> 2; case TV_SERIE -> 1; case CLOSE -> 3; @@ -94,8 +96,8 @@ public List getSortedSerieProviderIds(String searchQuery, Integ }; Map> serieProviderIds = api.getSerieProviderIds(searchQuery); List filteredResults = - serieProviderIds.get(TV_SERIE).stream().filter(subSceneSerieId -> Objects.equals(subSceneSerieId.season, - season)).toList(); + serieProviderIds.get(TV_SERIE).stream() + .filter(subSceneSerieId -> Objects.equals(subSceneSerieId.season, season)).toList(); if (filteredResults.size() == 1) { return filteredResults; } @@ -141,12 +143,12 @@ public String providerSerieIdToDisplayString(SubSceneSerieId providerSerieId) { @Override public SubsceneSubtitle convertToSubtitle(Release release, SubsceneSubtitleMetadata sub) { return new SubsceneSubtitle( - urlSupplier:sub.urlSupplier, - fileName:sub.name.removeIllegalFilenameChars(), - language:sub.language, - quality:ReleaseParser.getQualityKeyword(sub.name), - releaseGroup:ReleaseParser.extractReleaseGroup(sub.name, false), - uploader:sub.uploader, - hearingImpaired:sub.hearingImpaired); + sub.urlSupplier, + sub.name.removeIllegalFilenameChars(), + sub.language, + ReleaseParser.extractReleaseGroup(sub.name, false), + sub.uploader, + sub.hearingImpaired, + ReleaseParser.getQualityKeyword(sub.name)); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java index c27ae375..7a78ac52 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/SubsceneApi.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleApi; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneApiException; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model.SearchResultType; @@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory; import util.Utils; +@NullMarked public class SubsceneApi implements SubtitleApi { private static final Logger LOGGER = LoggerFactory.getLogger(SubtitleApi.class); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneApiException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneApiException.java index 96479fd6..b3245ec1 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneApiException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneApiException.java @@ -6,11 +6,14 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.CacheStrategy; import org.lodder.subtools.sublibrary.LogLevel; import org.lodder.subtools.sublibrary.util.http.ApiExceptionIntf; import org.lodder.subtools.sublibrary.util.http.HttpStatus; +@NullMarked public class SubsceneApiException extends SubsceneException implements ApiExceptionIntf { @val @override HttpStatus errorCode; @@ -25,7 +28,7 @@ public SubsceneApiException(HttpStatus errorCode, String errorMessage, CacheStra this.logLevel = logLevel; } - private SubsceneApiException(HttpStatus errorCode, Exception cause, String message= + private SubsceneApiException(HttpStatus errorCode, @Nullable Exception cause, String message= cause.getMessage(), CacheStrategy cacheStrategy, LogLevel logLevel) { super(message, cause); this.errorCode = errorCode; @@ -37,7 +40,7 @@ public static SubsceneApiException noResult(String message) { return new SubsceneApiException(NO_CONTENT, message, CACHE_TEMPORARY, WARN); } - public static SubsceneApiException error(Exception cause=null, String message=cause == null ? null : + public static SubsceneApiException error(@Nullable Exception cause=null, String message=cause == null ? null : cause.getMessage(), CacheStrategy cacheStrategy=CACHE_TEMPORARY) { return new SubsceneApiException(SERVER_ERROR, cause, message, cacheStrategy, ERROR); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java index 45bddeb3..7b1bdae4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/exception/SubsceneException.java @@ -4,9 +4,12 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class SubsceneException extends SubtitlesProviderException { @Serial @@ -22,7 +25,7 @@ public SubsceneException(Throwable cause) { super(cause); } - public SubsceneException(String message, Throwable cause) { + public SubsceneException(String message, @Nullable Throwable cause) { super(message, cause); } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SearchResultType.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SearchResultType.java index b8cf4360..98945beb 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SearchResultType.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SearchResultType.java @@ -1,7 +1,9 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.model; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public enum SearchResultType { EXACT("Exact"), TV_SERIE("TV-Serie"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java index f4e929a3..0414e188 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubSceneSerieId.java @@ -4,9 +4,11 @@ import java.time.YearMonth; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.data.ProviderId; +@NullMarked public class SubSceneSerieId extends ProviderId { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitle.java index 69048d73..25926b23 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitle.java @@ -6,7 +6,10 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; import name.falgout.jeffrey.throwing.ThrowingSupplier; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneException; import org.lodder.subtools.sublibrary.Language; @@ -14,25 +17,27 @@ import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class SubsceneSubtitle extends Subtitle { private final ThrowingSupplier urlSupplier; + @val @override SubtitleSource source = SubtitleSource.SUBSCENE; public SubsceneSubtitle(ThrowingSupplier urlSupplier, - @Nullable String fileName=null, - @Nullable Language language=null, - @Nullable String releaseGroup=null, - @Nullable String uploader=null, - boolean hearingImpaired=false, + String fileName, + Language language, + String releaseGroup, + String uploader, + boolean hearingImpaired, String quality) { - super(fileName, language, releaseGroup, uploader, SubtitleSource.SUBSCENE, hearingImpaired, quality); + super(fileName, language, releaseGroup, uploader, hearingImpaired, quality); this.urlSupplier = urlSupplier; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { try { String url = urlSupplier.get(); Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleMetadata.java index 3d3e2c78..ff7e6ecd 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/subscene/model/SubsceneSubtitleMetadata.java @@ -4,24 +4,25 @@ import manifold.ext.props.rt.api.val; import name.falgout.jeffrey.throwing.ThrowingSupplier; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.subtitleproviders.subscene.exception.SubsceneException; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.model.SeasonEpisode; +@NullMarked public class SubsceneSubtitleMetadata implements Serializable { - @val @Nullable Language language; - @val @Nullable String name; + @val Language language; + @val String name; @val boolean hearingImpaired; - @val @Nullable String uploader; - @val @Nullable String comment; + @val String uploader; + @val String comment; @val @Nullable SeasonEpisode seasonEpisode; @val ThrowingSupplier urlSupplier; - public SubsceneSubtitleMetadata(@Nullable Language language, - @Nullable String name, boolean hearingImpaired,@Nullable String uploader, @Nullable String comment, - ThrowingSupplier urlSupplier) { + public SubsceneSubtitleMetadata(Language language, String name, boolean hearingImpaired, String uploader, + String comment, ThrowingSupplier urlSupplier) { this.language = language; this.name = name; this.hearingImpaired = hearingImpaired; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TVSubtitlesLanguage.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TVSubtitlesLanguage.java index 1342d665..e72b0e6b 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TVSubtitlesLanguage.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TVSubtitlesLanguage.java @@ -3,8 +3,10 @@ import java.util.Optional; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.Language; +@NullMarked public enum TVSubtitlesLanguage { ENGLISH(Language.ENGLISH, "en"), SPANISH(Language.SPANISH_CASTILIAN, "es"), diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesAdapter.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesAdapter.java index 8d4ebdfc..2d1ccee8 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesAdapter.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesAdapter.java @@ -3,12 +3,12 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleAdapter; @@ -25,6 +25,7 @@ import org.lodder.subtools.sublibrary.model.SubtitleProviderFrontEnd; import org.lodder.subtools.sublibrary.settings.model.SerieMapping; +@NullMarked public final class TvSubtitlesAdapter extends SubtitleAdapter { @@ -83,8 +84,7 @@ public Collection searchSubtitles(SerieMapping seri } @Override - public List getSerieProviderIdById(ProviderIds providerIds, @Nullable Integer season) - throws TvSubtitleException { + public List getSerieProviderIdById(ProviderIds providerIds, @Nullable Integer season) { return List.of(); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesApi.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesApi.java index 2aa379c6..c92537ec 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesApi.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/TvSubtitlesApi.java @@ -33,6 +33,7 @@ /** * Api for retrieving serie information from tvsubtitles.net */ +@NullMarked public class TvSubtitlesApi implements SubtitleApi { private static final Logger LOGGER = LoggerFactory.getLogger(SubtitleApi.class); @@ -120,7 +121,8 @@ private List getSubtitles(String episodeUrl, Map metadataMap = subtitleElement.select(".subtitle_grid > div").stream().gather(Gatherers.windowFixed(3)) .map(values -> new Metadata(MetadataType.of(values.get(1).text()), - values.get(2).text())).filter(metadata -> metadata.metadataType != null) + values.get(2).text())) + .filter(metadata -> metadata.metadataType != null) .toMap(Metadata::metadataType, Metadata::value); return new TVSubtitlesSubtitleMetadata( metadataMap.get(MetadataType.TITLE), @@ -137,6 +139,7 @@ private List getSubtitles(String episodeUrl, }); } + @NullMarked private record Metadata(@Nullable MetadataType metadataType, String value) { } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleApiException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleApiException.java index f4519d37..fc5fd581 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleApiException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleApiException.java @@ -6,11 +6,13 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.CacheStrategy; import org.lodder.subtools.sublibrary.LogLevel; import org.lodder.subtools.sublibrary.util.http.ApiExceptionIntf; import org.lodder.subtools.sublibrary.util.http.HttpStatus; +@NullMarked public class TvSubtitleApiException extends TvSubtitleException implements ApiExceptionIntf { @val @override HttpStatus errorCode; diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleException.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleException.java index 988e07a7..e40477cc 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleException.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/exception/TvSubtitleException.java @@ -4,9 +4,11 @@ import manifold.ext.props.rt.api.override; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderException; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class TvSubtitleException extends SubtitlesProviderException { @Serial diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVSubtitlesSubtitleMetadata.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVSubtitlesSubtitleMetadata.java index cbd48932..65555bf4 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVSubtitlesSubtitleMetadata.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TVSubtitlesSubtitleMetadata.java @@ -2,9 +2,12 @@ import java.io.Serializable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.control.VideoPatterns.Source; -public record TVSubtitlesSubtitleMetadata(String title, String filename, String url, Source source, String releaseGroup, - Language language) implements Serializable { +@NullMarked +public record TVSubtitlesSubtitleMetadata(String title, String filename, String url, @Nullable Source source, + String releaseGroup, @Nullable Language language) implements Serializable { } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TvSubtiltesSubtitle.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TvSubtiltesSubtitle.java index 5f33a0ba..1d6e33a0 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TvSubtiltesSubtitle.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/subtitleproviders/tvsubtitles/model/TvSubtiltesSubtitle.java @@ -1,38 +1,71 @@ package org.lodder.subtools.multisubdownloader.subtitleproviders.tvsubtitles.model; import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.Path; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import manifold.ext.props.rt.api.override; +import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.model.Subtitle; import org.lodder.subtools.sublibrary.model.SubtitleSource; +@NullMarked public class TvSubtiltesSubtitle extends Subtitle { private final String url; + @val @override SubtitleSource source = SubtitleSource.TVSUBTITLES; public TvSubtiltesSubtitle(String url, - @Nullable String fileName=null, - @Nullable Language language=null, - @Nullable String releaseGroup=null, + String fileName, + Language language, + String releaseGroup, @Nullable String uploader=null, boolean hearingImpaired=false, - @Nullable String quality=null) { + String quality) { - super(fileName, language, releaseGroup, uploader, SubtitleSource.TVSUBTITLES, hearingImpaired, quality); + super(fileName, language, releaseGroup, uploader, hearingImpaired, quality); this.url = url; } @Override public List download(Manager manager, Path destinationFolder, - Function fileNameFunction) throws IOException { + Function<@Nullable AtomicInteger, String> fileNameFunction) throws IOException { Path subPath = destinationFolder.resolve(fileNameFunction.apply(null)); - manager.downloadAndExtractFile(url, subPath); + manager.downloadAndExtractFile(getForwardUrl(url), subPath); return List.of(subPath); } + + private String getForwardUrl(String url) throws IOException { + try { + URI uri = new URI(url); + try (InputStream inputStream = uri.toURL().openStream()) { + String pageContent = new String(inputStream.readAllBytes()); + + // Regex to extract string fragments from JS + Pattern pattern = Pattern.compile("var\\s+(s\\d)=\\s*'([^']+)'"); + Matcher matcher = pattern.matcher(pageContent); + + StringBuilder finalPath = new StringBuilder(); + while (matcher.find()) { + finalPath.append(matcher.group(2)); + } + + String baseUrl = uri.resolve(".").toString(); + return baseUrl + finalPath; + } + } catch (URISyntaxException e) { + throw new IOException(e); + } + } } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java index a7998ce7..6eadc03b 100755 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/ExportImport.java @@ -16,6 +16,7 @@ import com.google.gson.GsonBuilder; import io.gsonfire.GsonFireBuilder; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.gui.dialog.MappingEpisodeNameDialog.MappingType; import org.lodder.subtools.multisubdownloader.settings.SettingsControl; @@ -30,6 +31,7 @@ import org.lodder.subtools.sublibrary.util.filefilter.JsonFileFilter; import org.lodder.subtools.sublibrary.util.filefilter.XmlFileFilter; +@NullMarked public class ExportImport { private final Manager manager; @@ -45,6 +47,7 @@ public ExportImport(Manager manager, SettingsControl settingsControl, UserIntera this.parent = parent; } + @NullMarked public enum SettingsType { PREFERENCES(FileType.XML), SERIE_MAPPING(FileType.JSON); @@ -55,6 +58,7 @@ public enum SettingsType { } } + @NullMarked private enum FileType { XML(".xml", new XmlFileFilter()), JSON(".json", new JsonFileFilter()); @@ -109,6 +113,7 @@ public void exportSettings(SettingsType listType) { }); } + @NullMarked public static class ExportImportPreferences { private ExportImportPreferences() { @@ -129,6 +134,7 @@ public static void importSettings(Path path, UserInteractionHandler userInteract } } + @NullMarked public static class ExportImportSerieMapping { private ExportImportSerieMapping() { @@ -171,6 +177,7 @@ private static Optional getImportStyle(UserInteractionHandler userI }); } + @NullMarked private record SerieMappingWithKey(ProviderCacheKey key, SerieMapping serieMapping) { } } @@ -188,10 +195,12 @@ private Optional chooseFile(ExportImport.FileType fileType) { } } + @NullMarked private enum ImportStyle { OVERWRITE, APPEND } + @NullMarked public static class CorruptSettingsFileException extends Exception { public CorruptSettingsFileException(Throwable cause) { super(cause); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/MapUtil.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/MapUtil.java index 974bba17..11f76756 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/MapUtil.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/MapUtil.java @@ -3,6 +3,9 @@ import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@NullMarked public class MapUtil { private MapUtil(){ diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java index 5536bfd2..3bc8d924 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/util/PropertiesReader.java @@ -5,7 +5,9 @@ import java.util.Properties; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; +@NullMarked public class PropertiesReader { private static PropertiesReader propertiesReaderInstance; @@ -33,6 +35,7 @@ public static String getProperty(PomProperty property) { return PropertiesReader.getPropertiesReader().properties.getProperty(property.value); } + @NullMarked public enum PomProperty { BUILD_TIMESTAMP("build.timestamp"); diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchHandler.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchHandler.java index dda2043a..c13ce10e 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchHandler.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchHandler.java @@ -2,9 +2,11 @@ import java.util.List; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; -public interface SearchHandler { - void onFound(Release release, List subtitles); +@NullMarked +public interface SearchHandler { + void onFound(R release, List subtitles); } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java index c421e040..5cd18a8c 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchManager.java @@ -13,6 +13,8 @@ import manifold.ext.props.rt.api.set; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.UserInteractionHandler; import org.lodder.subtools.multisubdownloader.gui.dialog.Cancelable; import org.lodder.subtools.multisubdownloader.lib.control.subtitles.sorting.ScoreCalculator; @@ -24,6 +26,7 @@ import org.lodder.subtools.sublibrary.model.Release; import org.lodder.subtools.sublibrary.model.Subtitle; +@NullMarked public class SearchManager implements Cancelable { private final Map> queue = new HashMap<>(); @@ -102,11 +105,11 @@ public boolean cancel(boolean mayInterruptIfRunning) { return true; } - public Release getNextRelease(SubtitleProvider provider) { + public @Nullable Release getNextRelease(SubtitleProvider provider) { synchronized (provider) { if (!this.hasNextRelease(provider)) { /* Tell the progressListener this provider is finished */ - this.progressListener.progress(provider, queue.get(provider).size(), null); + this.progressListener.done(provider); return null; } diff --git a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java index a942a234..b461fcac 100644 --- a/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java +++ b/MultiSubDownloader/src/main/java/org/lodder/subtools/multisubdownloader/workers/SearchWorker.java @@ -7,6 +7,7 @@ import manifold.ext.props.rt.api.set; import manifold.ext.props.rt.api.val; import manifold.ext.props.rt.api.var; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.multisubdownloader.subtitleproviders.SubtitleProvider; import org.lodder.subtools.sublibrary.Language; import org.lodder.subtools.sublibrary.exception.SubtitlesProviderInitException; @@ -15,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class SearchWorker extends Thread { private static final Logger LOGGER = LoggerFactory.getLogger(SearchWorker.class); @@ -54,7 +56,7 @@ public void run() { this.busy = false; LOGGER.debug("[Search] {} found {} subtitles for {} ", this.provider.provider, subtitles.size(), - release); + release); if (!this.isInterrupted()) { this.scheduler.onCompleted(this); diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java index 277c28ef..1c000cb9 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/SubtitleFilteringTest.java @@ -5,11 +5,12 @@ import java.util.List; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Test; import org.lodder.subtools.multisubdownloader.settings.model.Settings; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.Subtitle; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; class SubtitleFilteringTest { @@ -20,88 +21,89 @@ void testExcludeImpairedHearingFiltering() { Subtitle subtitle3 = createSubtitle("", "", true, ""); assertThatFilter(new SubtitleFiltering(createSettings(false, false, true))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3) - .forRelease(createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION")) - .matchesSubtitles(subtitle2); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3) + .forRelease(createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION")) + .matchesSubtitles(subtitle2); } @Test void testKeywordMatchFilter() { - Release release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); + ReleaseWithoutPath release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); Subtitle subtitle3 = - createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); Subtitle subtitle4 = - createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); Subtitle subtitle5 = createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, - "720p HDTV X264"); + "720p HDTV X264"); Subtitle subtitle6 = - createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only keyword assertThatFilter(new SubtitleFiltering(createSettings(true, false, false))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) - .forRelease(release) - .matchesSubtitles(subtitle3, subtitle4, subtitle5); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) + .forRelease(release) + .matchesSubtitles(subtitle3, subtitle4, subtitle5); // keyword and exclude hearing impaired assertThatFilter(new SubtitleFiltering(createSettings(true, false, true))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) - .forRelease(release) - .matchesSubtitles(subtitle4, subtitle5); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) + .forRelease(release) + .matchesSubtitles(subtitle4, subtitle5); } @Test void testExactMatchFilter() { - Release release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); + ReleaseWithoutPath release = createRelease("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); Subtitle subtitle3 = - createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); Subtitle subtitle4 = - createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); Subtitle subtitle5 = - createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only exact match assertThatFilter(new SubtitleFiltering(createSettings(false, true, false))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5) - .forRelease(release) - .matchesSubtitles(subtitle3, subtitle4); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5) + .forRelease(release) + .matchesSubtitles(subtitle3, subtitle4); // exact match and exclude hearing impaired assertThatFilter(new SubtitleFiltering(createSettings(false, true, true))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5) - .forRelease(release) - .matchesSubtitles(subtitle4); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5) + .forRelease(release) + .matchesSubtitles(subtitle4); } @Test void testExactMatchAndKeywordMatchFilter() { - Release release = createRelease("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); + ReleaseWithoutPath release = + createRelease("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.mkv", "DIMENSION"); Subtitle subtitle1 = createSubtitle("Criminal.Minds.S10E12.HDTV.XviD-AFG.srt", "AFG", false, ""); Subtitle subtitle2 = createSubtitle("criminal.minds.1012.hdtv-lol.srt", "lol", false, ""); Subtitle subtitle3 = - createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); + createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", true, ""); Subtitle subtitle4 = - createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + createSubtitle("Criminal.Minds.S10E12.Anonymous.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); Subtitle subtitle5 = - createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); + createSubtitle("Criminal.Minds.S10E12.720p.HDTV.X264-DIMENSION.srt", "DIMENSION", false, ""); Subtitle subtitle6 = - createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); + createSubtitle("Criminal.Minds.S10E12.Anonymous.1080p.WEB-DL.DD5.1.H.264-CtrlHD", "CtrlHD", false, ""); // only exact match assertThatFilter(new SubtitleFiltering(createSettings(true, true, false))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) - .forRelease(release) - .matchesSubtitles(subtitle3, subtitle4); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) + .forRelease(release) + .matchesSubtitles(subtitle3, subtitle4); // exact match and exclude hearing impaired assertThatFilter(new SubtitleFiltering(createSettings(true, true, true))) - .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) - .forRelease(release) - .matchesSubtitles(subtitle4); + .appliedOnSubtitles(subtitle1, subtitle2, subtitle3, subtitle4, subtitle5, subtitle6) + .forRelease(release) + .matchesSubtitles(subtitle4); } private Settings createSettings(boolean keyword, boolean exact, boolean excludehearing) { @@ -114,10 +116,10 @@ private Settings createSettings(boolean keyword, boolean exact, boolean excludeh return settings; } - private Release createRelease(String filename, String releasegroup) { - Release release = mock(TvRelease.class); + private ReleaseWithoutPath createRelease(String filename, String releasegroup) { + ReleaseWithoutPath release = mock(TvReleaseWithoutPath.class); - when(release.fileName).thenReturn(filename); + when(release.fileNameOrName).thenReturn(filename); when(release.releaseGroup).thenReturn(releasegroup); return release; @@ -139,48 +141,55 @@ private TestSetupSubtitlesIntf assertThatFilter(SubtitleFiltering filter) { return new TestSetupFiltering().assertThatFilter(filter); } + @NullMarked private interface TestSetupSubtitlesIntf { TestSetupReleaseIntf appliedOnSubtitles(Subtitle... subtitles); } + @NullMarked private interface TestSetupReleaseIntf { - TestSetupMatchesIntf forRelease(Release release); + TestSetupMatchesIntf forRelease(ReleaseWithoutPath release); } + @NullMarked private interface TestSetupMatchesIntf { void matchesSubtitles(Subtitle... subtitles); } + @NullMarked private static class TestSetupFiltering - implements TestSetupSubtitlesIntf, TestSetupReleaseIntf, TestSetupMatchesIntf { + implements TestSetupSubtitlesIntf, TestSetupReleaseIntf, TestSetupMatchesIntf { private SubtitleFiltering filter; private List subtitles; - private Release release; + private ReleaseWithoutPath release; public TestSetupFiltering assertThatFilter(SubtitleFiltering filter) { this.filter = filter; return this; } + @Override public TestSetupFiltering appliedOnSubtitles(Subtitle... subtitles) { this.subtitles = subtitles.stream().toList(); return this; } - public TestSetupFiltering forRelease(Release release) { + @Override + public TestSetupFiltering forRelease(ReleaseWithoutPath release) { this.release = release; return this; } + @Override public void matchesSubtitles(Subtitle... subtitles) { List filteredSubtitles = - this.subtitles.stream().filter(subtitle -> filter.useSubtitle(subtitle, release)).toList(); + this.subtitles.stream().filter(subtitle -> filter.useSubtitle(subtitle, release)).toList(); assertThat(filteredSubtitles) - .withFailMessage("Expected the filtered subtitles to contain exactly %s, but found %s".formatted( - subtitles.stream().map(Subtitle::getFileName).toList(), - filteredSubtitles.stream().map(Subtitle::getFileName).toList())) - .containsExactlyInAnyOrder(subtitles); + .withFailMessage("Expected the filtered subtitles to contain exactly %s, but found %s".formatted( + subtitles.stream().map(Subtitle::getFileName).toList(), + filteredSubtitles.stream().map(Subtitle::getFileName).toList())) + .containsExactlyInAnyOrder(subtitles); } } } diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java index 01adc265..ba2e32c0 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/ScoreCalculatorTest.java @@ -6,9 +6,9 @@ import java.util.HashMap; import org.junit.jupiter.api.Test; -import org.lodder.subtools.sublibrary.model.Release; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; import org.lodder.subtools.sublibrary.model.Subtitle; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; class ScoreCalculatorTest { @@ -53,7 +53,7 @@ private Subtitle createSubtitle(String filename, String quality, String team) { private SortWeight createWeights(String quality, String group) { // Arrested.Development.S01E01.DVDRip.XviD-MEDiEVAL - Release release = mock(TvRelease.class); + ReleaseWithoutPath release = mock(TvReleaseWithoutPath.class); when(release.quality).thenReturn(quality); when(release.releaseGroup).thenReturn(group); diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java index 24bc96f6..24a32664 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/SortWeightTest.java @@ -7,15 +7,15 @@ import java.util.Map; import org.junit.jupiter.api.Test; -import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; class SortWeightTest { @Test void test_it_generates_weights_for_release() throws Exception { // Arrested.Development.S01E01.DVDRip.XviD-MEDiEVAL - Release release = mock(TvRelease.class); + ReleaseWithoutPath release = mock(TvReleaseWithoutPath.class); when(release.quality).thenReturn("DVDRip XviD"); when(release.releaseGroup).thenReturn("MEDiEVAL"); diff --git a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java index 255f5dee..23227641 100644 --- a/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java +++ b/MultiSubDownloader/src/test/java/org/lodder/subtools/multisubdownloader/lib/control/subtitles/sorting/replacers/GroupReplacerTest.java @@ -7,8 +7,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.lodder.subtools.sublibrary.model.Release; -import org.lodder.subtools.sublibrary.model.TvRelease; +import org.lodder.subtools.sublibrary.model.ReleaseWithoutPath; +import org.lodder.subtools.sublibrary.model.TvReleaseWithoutPath; class GroupReplacerTest { @@ -21,7 +21,7 @@ public void setUp() { @Test void test_it_replaces_the_keyword_group_to_a_releasename() { - Release release = mock(TvRelease.class); + ReleaseWithoutPath release = mock(TvReleaseWithoutPath.class); when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); @@ -38,7 +38,7 @@ void test_it_replaces_the_keyword_group_to_a_releasename() { @Test void testEmptyWeights() { - Release release = mock(TvRelease.class); + ReleaseWithoutPath release = mock(TvReleaseWithoutPath.class); when(release.releaseGroup).thenReturn("Acme"); HashMap definedWeights = new HashMap<>(); diff --git a/SubLibrary/pom.xml b/SubLibrary/pom.xml index cfd10b6e..c16b7640 100644 --- a/SubLibrary/pom.xml +++ b/SubLibrary/pom.xml @@ -151,6 +151,14 @@ com.squareup.retrofit2 converter-gson + + org.htmlunit + htmlunit + + + org.jetbrains + annotations + org.mockito diff --git a/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java b/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java index a1a63e22..3ac4c96a 100644 --- a/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java +++ b/SubLibrary/src/main/java/extensions/java/io/InputStream/InputStreamExt.java @@ -4,14 +4,19 @@ import java.io.InputStream; import java.nio.charset.Charset; -//@Extension +import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; + +@Extension +@NullMarked public class InputStreamExt { private InputStreamExt() { // hide utility class constructor } - public static String asString(InputStream inputStream, Charset charset) throws IOException { + public static String asString(@This InputStream inputStream, Charset charset) throws IOException { return new String(inputStream.readAllBytes(), charset); } } diff --git a/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java index 0ca2134a..c6fc063a 100644 --- a/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java +++ b/SubLibrary/src/main/java/extensions/java/lang/Iterable/IterableExt.java @@ -9,8 +9,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; import name.falgout.jeffrey.throwing.ThrowingConsumer; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class IterableExt { private IterableExt() { diff --git a/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java index 11e5b57a..ad381260 100644 --- a/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java +++ b/SubLibrary/src/main/java/extensions/java/lang/String/StringExt.java @@ -11,9 +11,11 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @Extension +@NullMarked public class StringExt { private StringExt() { diff --git a/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java b/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java index f95e18e5..3a93394e 100644 --- a/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java +++ b/SubLibrary/src/main/java/extensions/java/nio/file/Path/PathExt.java @@ -21,11 +21,13 @@ import name.falgout.jeffrey.throwing.ThrowingConsumer; import name.falgout.jeffrey.throwing.ThrowingFunction; import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.util.CopyDirVisitor; import org.lodder.subtools.sublibrary.util.DeleteDirVisitor; @Extension @ExtensionSource(source = Files.class, methods = {@MethodSignature(name = "size", paramTypes = {Path.class})}) +@NullMarked public class PathExt { private PathExt() { @@ -48,6 +50,7 @@ public static String withoutExtension(@This Path path) { return changeExtension(path, ""); } + @NullMarked public record FilenameAndExtension(String filename, String extension) { } @@ -344,4 +347,12 @@ public static boolean isGZipCompressed(byte[] data) { return data.length >= 2 && data[0] == (byte) GZIPInputStream.GZIP_MAGIC && data[1] == (byte) GZIPInputStream.GZIP_MAGIC >> 8; } + + public static Path resolve(@This Path path, String... subPaths) { + Path fullPath = path; + for (String subPath : subPaths) { + fullPath = fullPath.resolve(subPath); + } + return fullPath; + } } diff --git a/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java b/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java index ad548f40..db93b004 100644 --- a/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/Collection/CollectionExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class CollectionExt { public static @Self Collection replaceContents(@This Collection collection, Collection values) { diff --git a/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java b/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java index 918095a6..1d4139ac 100644 --- a/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/Iterator/IteratorExt.java @@ -7,8 +7,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class IteratorExt { private IteratorExt() { diff --git a/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java index 26ca1279..e92eda64 100644 --- a/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/Optional/OptionalExt.java @@ -2,6 +2,7 @@ import java.util.Optional; import java.util.OptionalInt; +import java.util.function.Function; import java.util.function.Supplier; import manifold.ext.rt.api.Extension; @@ -12,13 +13,17 @@ import name.falgout.jeffrey.throwing.ThrowingRunnable; import name.falgout.jeffrey.throwing.ThrowingSupplier; import name.falgout.jeffrey.throwing.ThrowingToIntFunction; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) @Extension +@NullMarked public class OptionalExt { private OptionalExt() { - // hide utility class constructor + // Hide Utility Class Constructor } // /** @@ -33,36 +38,37 @@ private OptionalExt() { // return optional.map(function); // } + public static Optional filterCast(@This Optional optional, Class type) { + return optional.filter(type::isInstance).map(type::cast); + } + /** - * Applies the given {@link ThrowingFunction} to the value if present. + * Applies the given {@link Function} to the value if present. * * @param optional the input {@code Optional} for the extension method - * @param function the function to apply + * @param mapper the function to apply * @param the type of the input value - * @param the type of exception the function may throw + * @param the type of the output value * @return an {@code Optional} describing the result of the function, or empty if not present - * @throws X if the function throws an exception */ - public static Optional mapEx(@This Optional optional, - ThrowingFunction function) throws X { - return optional.isPresent() ? Optional.ofNullable(function.apply(optional.get())) : Optional.empty(); + public static Optional<@NonNull U> mapNullable(@This Optional optional, + Function mapper) { + return optional.map(mapper::apply); } /** - * Applies the given {@link ThrowingFunction} to the value if present. If an exception is thrown, consume it. + * Applies the given {@link ThrowingFunction} to the value if present. * * @param optional the input {@code Optional} for the extension method * @param function the function to apply * @param the type of the input value + * @param the type of exception the function may throw * @return an {@code Optional} describing the result of the function, or empty if not present + * @throws X if the function throws an exception */ - public static Optional mapConsumeEx(@This Optional optional, - ThrowingFunction function) { - try { - return optional.mapEx(function); - } catch (Exception e) { - return Optional.empty(); - } + public static Optional mapEx( + @This Optional optional, ThrowingFunction function) throws X { + return optional.isPresent() ? Optional.ofNullable(function.apply(optional.get())) : Optional.empty(); } /** @@ -75,8 +81,8 @@ public static Optional mapConsumeEx(@This Optiona * @return an {@code Optional} describing the result of the function, or empty if not present * @throws X if the function throws an exception */ - public static Optional flatMapEx(@This Optional optional, - ThrowingFunction, X> function) throws X { + public static Optional flatMapEx( + @This Optional optional, ThrowingFunction, X> function) throws X { return optional.isPresent() ? function.apply(optional.get()) : Optional.empty(); } @@ -88,7 +94,8 @@ public static Optional flatMapEx(@This Optional the type of the value * @return the current value or the result of the supplier */ - public static Optional orElseMap(@This Optional optional, Supplier> supplier) { + public static Optional orElseMap(@This Optional optional, + Supplier> supplier) { return optional.isPresent() ? optional : supplier.get(); } @@ -102,7 +109,7 @@ public static Optional orElseMap(@This Optional optional, Supplier Optional orElseMapEx(@This Optional optional, + public static Optional orElseMapEx(@This Optional optional, ThrowingSupplier, X> supplier) throws X { return optional.isPresent() ? optional : supplier.get(); } @@ -114,7 +121,7 @@ public static Optional orElseMapEx(@This Optional * @param runnable the action to perform if the value is absent * @param the type of the value */ - public static void ifNotPresent(@This Optional optional, Runnable runnable) { + public static void ifNotPresent(@This Optional optional, Runnable runnable) { if (optional.isEmpty()) { runnable.run(); } @@ -129,36 +136,60 @@ public static void ifNotPresent(@This Optional optional, Runnable runnabl * @param the type of exception the runnable may throw * @throws X if the runnable throws an exception */ - public static void ifNotPresentEx(@This Optional optional, + public static void ifNotPresentEx(@This Optional optional, ThrowingRunnable runnable) throws X { if (optional.isEmpty()) { runnable.run(); } } - public static OptionalInt mapToIntEx(@This Optional optional, + public static OptionalInt mapToIntEx(@This Optional optional, ThrowingToIntFunction mapper) throws X { return optional.isPresent() ? OptionalInt.of(mapper.applyAsInt(optional.get())) : OptionalInt.empty(); } - public static @Self Optional useIfPresentEx(@This Optional optional, - ThrowingConsumer consumer) throws X { + public static @Self Optional useIfPresentEx( + @This Optional optional, ThrowingConsumer consumer) throws X { if (optional.isPresent()) { consumer.accept(optional.get()); } return optional; } - public static T orElseGetEx(@This Optional optional, + public static T orElseGetEx(@This Optional optional, ThrowingSupplier supplier) throws X { return optional.isPresent() ? optional.get() : supplier.get(); } - public static void ifPresentEx(@This Optional optional, - ThrowingConsumer supplier) throws X { + public static void ifPresentEx(@This Optional optional, + ThrowingConsumer consumer) throws X { + if (optional.isPresent()) { + consumer.accept(optional.get()); + } + } + + /** + * If a value is present in the given {@link Optional}, performs the provided + * {@code consumer} with the value; otherwise performs the provided + * {@code elseRunnable}. + * + *

This method is similar to {@link Optional#ifPresentOrElse}, but allows + * both actions to throw checked exceptions.

+ * + * @param the type of the value contained in the optional + * @param the type of exception that may be thrown by either action + * @param optional the optional to inspect + * @param consumer the action to execute if a value is present + * @param elseRunnable the action to execute if no value is present + * @throws X if the consumer or elseRunnable throws an exception + */ + public static void ifPresentOrElseEx(@This Optional optional, + ThrowingConsumer consumer, ThrowingRunnable elseRunnable) throws X { if (optional.isPresent()) { - supplier.accept(optional.get()); + consumer.accept(optional.get()); + } else { + elseRunnable.run(); } } } diff --git a/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java b/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java index 6e5041ff..97fdec5c 100644 --- a/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/OptionalInt/OptionalIntExt.java @@ -8,38 +8,46 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingIntConsumer; import name.falgout.jeffrey.throwing.ThrowingIntFunction; import name.falgout.jeffrey.throwing.ThrowingIntUnaryOperator; import name.falgout.jeffrey.throwing.ThrowingRunnable; import name.falgout.jeffrey.throwing.ThrowingSupplier; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @Extension -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) +@NullMarked public class OptionalIntExt { private OptionalIntExt() { - // hide utility class constructor + // Hide Utility Class Constructor } /** - * If a value is present, applies the {@link IntUnaryOperator} to it and returns the result wrapped in an {@link OptionalInt}. + * If a value is present, applies the {@link IntUnaryOperator} to it and returns the result wrapped in an + * {@link OptionalInt}. * * @param optional the input {@code OptionalInt} for the extension method * @param function the function to apply if a value is present - * @return an {@code OptionalInt} describing the result of applying the function, or an empty {@code OptionalInt} if no value is present + * @return an {@code OptionalInt} describing the result of applying the function, or an empty {@code OptionalInt} + * if no value is present */ public static OptionalInt map(@This OptionalInt optional, IntUnaryOperator function) { return optional.isPresent() ? OptionalInt.of(function.applyAsInt(optional.getAsInt())) : OptionalInt.empty(); } /** - * If a value is present, applies the {@link ThrowingIntUnaryOperator} to it and returns the result wrapped in an {@link OptionalInt}. + * If a value is present, applies the {@link ThrowingIntUnaryOperator} to it and returns the result wrapped in an + * {@link OptionalInt}. * * @param optional the input {@code OptionalInt} for the extension method * @param function the function to apply if a value is present * @param the type of exception that the function may throw - * @return an {@code OptionalInt} describing the result of applying the function, or an empty {@code OptionalInt} if no value is present + * @return an {@code OptionalInt} describing the result of applying the function, or an empty {@code OptionalInt} + * if no value is present * @throws X if the function throws an exception */ public static OptionalInt mapEx(@This OptionalInt optional, @@ -50,45 +58,66 @@ public static OptionalInt mapEx(@This OptionalInt optional /** - * If a value is present, applies the {@link IntFunction} to it and returns the result wrapped in an {@link Optional}. + * If a value is present, applies the {@link IntFunction} to it and returns the result wrapped in an + * {@link Optional}. * * @param optional the input {@code OptionalInt} for the extension method * @param function the function to apply if a value is present * @param the type of the result - * @return an {@code Optional} describing the result of applying the function, or an empty {@code Optional} if no value is present + * @return an {@code Optional} describing the result of applying the function, or an empty {@code Optional} if no + * value is present */ - public static Optional mapToObj(@This OptionalInt optional, IntFunction function) { + public static Optional mapToObj(@This OptionalInt optional, + IntFunction function) { return optional.isPresent() ? Optional.ofNullable(function.apply(optional.getAsInt())) : Optional.empty(); } /** - * If a value is present, applies the {@link ThrowingIntFunction} to it and returns the result wrapped in an {@link Optional}. + * If a value is present, applies the {@link ThrowingIntFunction} to it and returns the result wrapped in an + * {@link Optional}. * * @param optional the input {@code OptionalInt} for the extension method * @param function the function to apply if a value is present * @param the type of the result * @param the type of exception that the function may throw - * @return an {@code Optional} describing the result of applying the function, or an empty {@code Optional} if no value is present + * @return an {@code Optional} describing the result of applying the function, or an empty {@code Optional} if no + * value is present * @throws X if the function throws an exception */ - public static Optional mapToObjEx(@This OptionalInt optional, + public static Optional mapToObjEx(@This OptionalInt optional, ThrowingIntFunction function) throws X { return optional.isPresent() ? Optional.ofNullable(function.apply(optional.getAsInt())) : Optional.empty(); } /** - * If a value is present, applies the {@link ThrowingIntFunction} to it and returns the result wrapped in an {@link Optional}. + * If a value is present, applies the {@link IntFunction} to it and returns the result wrapped in an + * {@link Optional}. * - * @param optional the input {@code OptionalInt} for the extension method + * @param optionalInt the input {@code OptionalInt} for the extension method + * @param function the function to apply if a value is present + * @param the type of the result + * @param the type of exception that the function may throw + * @return an {@code Optional} describing the result of applying the function + */ + public static Optional flatMapToObj( + @This OptionalInt optionalInt, IntFunction> function) throws X { + return optionalInt.isPresent() ? function.apply(optionalInt.getAsInt()) : Optional.empty(); + } + + /** + * If a value is present, applies the {@link ThrowingIntFunction} to it and returns the result wrapped in an + * {@link Optional}. + * + * @param optionalInt the input {@code OptionalInt} for the extension method * @param function the function to apply if a value is present * @param the type of the result * @param the type of exception that the function may throw - * @return an {@code Optional} describing the result of applying the function, or an empty {@code Optional} if no value is present + * @return an {@code Optional} describing the result of applying the function * @throws X if the function throws an exception */ - public static Optional flatMapToObjEx(@This OptionalInt optional, - ThrowingIntFunction, X> function) throws X { - return optional.isPresent() ? function.apply(optional.getAsInt()) : Optional.empty(); + public static Optional flatMapToObjEx( + @This OptionalInt optionalInt, ThrowingIntFunction, X> function) throws X { + return optionalInt.isPresent() ? function.apply(optionalInt.getAsInt()) : Optional.empty(); } /** @@ -105,12 +134,6 @@ public static OptionalInt orElseMapEx(@This OptionalInt op return optionalInt.isPresent() ? optionalInt : intSupplier.get(); } - - public static Optional flatMap(@This OptionalInt optionalInt, - IntFunction> mapper) throws X { - return optionalInt.isPresent() ? mapper.apply(optionalInt.getAsInt()) : Optional.empty(); - } - /** * Executes the given {@link Runnable} if the {@code OptionalInt} is empty. * @@ -137,4 +160,27 @@ public static void ifNotPresentEx(@This OptionalInt option runnable.run(); } } + + /** + * If a value is present in the given {@link OptionalInt}, performs the provided + * {@code consumer} with the value; otherwise performs the provided + * {@code elseRunnable}. + * + *

This method is similar to {@link OptionalInt#ifPresentOrElse}, but allows + * both actions to throw checked exceptions.

+ * + * @param the type of exception that may be thrown by either action + * @param optional the optional to inspect + * @param consumer the action to execute if a value is present + * @param elseRunnable the action to execute if no value is present + * @throws X if the consumer or elseRunnable throws an exception + */ + public static void ifPresentOrElseEx(@This OptionalInt optional, + ThrowingIntConsumer consumer, ThrowingRunnable elseRunnable) throws X { + if (optional.isPresent()) { + consumer.accept(optional.getAsInt()); + } else { + elseRunnable.run(); + } + } } diff --git a/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java b/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java index b84d2c50..b5463330 100644 --- a/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/OptionalLong/OptionalLongExt.java @@ -7,17 +7,21 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import name.falgout.jeffrey.throwing.ThrowingLongConsumer; import name.falgout.jeffrey.throwing.ThrowingLongFunction; import name.falgout.jeffrey.throwing.ThrowingLongUnaryOperator; import name.falgout.jeffrey.throwing.ThrowingRunnable; import name.falgout.jeffrey.throwing.ThrowingSupplier; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @Extension -@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) +@NullMarked public class OptionalLongExt { private OptionalLongExt() { - // hide utility class constructor + // Hide Utility Class Constructor } /** @@ -55,7 +59,8 @@ public static OptionalLong mapEx(@This OptionalLong option * @param the result type * @return an {@code Optional} describing the mapped result, or empty if not present */ - public static Optional mapToObj(@This OptionalLong optional, LongFunction function) { + public static Optional mapToObj(@This OptionalLong optional, + LongFunction function) { return optional.isPresent() ? Optional.ofNullable(function.apply(optional.getAsLong())) : Optional.empty(); } @@ -69,11 +74,43 @@ public static Optional mapToObj(@This OptionalLong optional, LongFunction * @return an {@code Optional} describing the mapped result, or empty if not present * @throws X if the function throws an exception */ - public static Optional mapToObjEx(@This OptionalLong optional, + public static Optional mapToObjEx(@This OptionalLong optional, ThrowingLongFunction function) throws X { return optional.isPresent() ? Optional.ofNullable(function.apply(optional.getAsLong())) : Optional.empty(); } + /** + * If a value is present, applies the {@link LongFunction} to it and returns the result wrapped in an + * {@link Optional}. + * + * @param optionalLong the input {@code OptionalLong} for the extension method + * @param function the function to apply if a value is present + * @param the type of the result + * @param the type of exception that the function may throw + * @return an {@code Optional} describing the result of applying the function + */ + public static Optional flatMapToObj( + @This OptionalLong optionalLong, LongFunction> function) throws X { + return optionalLong.isPresent() ? function.apply(optionalLong.getAsLong()) : Optional.empty(); + } + + /** + * If a value is present, applies the {@link ThrowingLongFunction} to it and returns the result wrapped in an + * {@link Optional}. + * + * @param optionalLong the input {@code OptionalLong} for the extension method + * @param function the function to apply if a value is present + * @param the type of the result + * @param the type of exception that the function may throw + * @return an {@code Optional} describing the result of applying the function + * @throws X if the function throws an exception + */ + public static Optional flatMapToObjEx( + @This OptionalLong optionalLong, ThrowingLongFunction, X> function) throws X { + return optionalLong.isPresent() ? function.apply(optionalLong.getAsLong()) : Optional.empty(); + } + + /** * Returns the current {@code OptionalLong} if a value is present, otherwise returns the result of the supplier. * @@ -114,4 +151,27 @@ public static void ifNotPresentEx(@This OptionalLong optio runnable.run(); } } + + /** + * If a value is present in the given {@link OptionalLong}, performs the provided + * {@code consumer} with the value; otherwise performs the provided + * {@code elseRunnable}. + * + *

This method is similar to {@link OptionalLong#ifPresentOrElse}, but allows + * both actions to throw checked exceptions.

+ * + * @param the type of exception that may be thrown by either action + * @param optional the optional to inspect + * @param consumer the action to execute if a value is present + * @param elseRunnable the action to execute if no value is present + * @throws X if the consumer or elseRunnable throws an exception + */ + public static void ifPresentOrElseEx(@This OptionalLong optional, + ThrowingLongConsumer consumer, ThrowingRunnable elseRunnable) throws X { + if (optional.isPresent()) { + consumer.accept(optional.getAsLong()); + } else { + elseRunnable.run(); + } + } } diff --git a/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java b/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java index 06c8a76c..bdf9dacf 100644 --- a/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/Spliterator/SpliteratorExt.java @@ -6,8 +6,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class SpliteratorExt { private SpliteratorExt() { diff --git a/SubLibrary/src/main/java/extensions/java/util/prefs/Preferences/PreferencesExt.java b/SubLibrary/src/main/java/extensions/java/util/prefs/Preferences/PreferencesExt.java index 99649ebc..e5ee0d0c 100644 --- a/SubLibrary/src/main/java/extensions/java/util/prefs/Preferences/PreferencesExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/prefs/Preferences/PreferencesExt.java @@ -5,8 +5,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class PreferencesExt { private PreferencesExt() { diff --git a/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java b/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java index d992a055..791e35b1 100644 --- a/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java +++ b/SubLibrary/src/main/java/extensions/java/util/stream/Stream/StreamExt.java @@ -1,29 +1,69 @@ package extensions.java.util.stream.Stream; +import static java.util.function.Predicate.*; import static util.SneakyThrowUtil.*; +import static util.Utils.*; import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.DoubleStream; +import java.util.stream.Gatherer; +import java.util.stream.IntStream; +import java.util.stream.LongStream; import java.util.stream.Stream; import manifold.ext.rt.api.Extension; +import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; import name.falgout.jeffrey.throwing.ThrowingConsumer; import name.falgout.jeffrey.throwing.ThrowingFunction; +import name.falgout.jeffrey.throwing.ThrowingPredicate; +import name.falgout.jeffrey.throwing.ThrowingToDoubleFunction; +import name.falgout.jeffrey.throwing.ThrowingToIntFunction; +import name.falgout.jeffrey.throwing.ThrowingToLongFunction; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@SuppressWarnings("unused") @Extension +@NullMarked public class StreamExt { private StreamExt() { - // hide utility class constructor + // Hide Utility Class Constructor } - public static Stream mapEx(@This Stream stream, - ThrowingFunction mapper) throws X { + // MAP // + + public static Stream mapFilterNonNull(@This Stream stream, + Function mapper) { + return stream.gather(Gatherer.ofSequential((_, element, downstream) -> { + ifNotNullDo(mapper.apply(element), downstream::push); + return true; + })); + } + + public static Stream mapFilterNonNullEx( + @This Stream stream, ThrowingFunction mapper) throws X { + return stream.gather(Gatherer.ofSequential((_, element, downstream) -> { + try { + ifNotNullDo(mapper.apply(element), downstream::push); + } catch (Exception e) { + sneakyThrow(e); + } + return true; + })); + } + + public static Stream mapEx( + @This Stream stream, ThrowingFunction mapper) throws X { return stream.map(sneaky(mapper)); } - public static Stream mapIgnoreEx(@This Stream stream, - ThrowingFunction mapper) { + public static Stream mapIgnoreEx( + @This Stream stream, ThrowingFunction mapper) { try { return stream.mapEx(mapper); } catch (Exception e) { @@ -31,13 +71,15 @@ public static Stream mapIgnoreEx(@This Stream } } - public static , V, X extends Exception> Stream flatMapEx(@This Stream stream, - ThrowingFunction mapper) throws X { + // FLATMAP // + + public static , V extends @Nullable Object, X extends Exception> Stream flatMapEx( + @This Stream stream, ThrowingFunction mapper) throws X { return stream.flatMap(sneaky(mapper)); } - public static , V, X extends Exception> Stream flatMapIgnoreEx(@This Stream stream, - ThrowingFunction mapper) { + public static , V extends @Nullable Object, X extends Exception> Stream flatMapIgnoreEx( + @This Stream stream, ThrowingFunction mapper) { try { return stream.flatMapEx(mapper); } catch (Exception e) { @@ -45,12 +87,14 @@ public static , V, X extends Exception> Stream flatMap } } - public static , V, X extends Exception> Stream flatMapOptionalEx(@This Stream stream, - ThrowingFunction mapper) throws X { + public static , V extends @Nullable Object, + X extends Exception> Stream flatMapOptionalEx( + @This Stream stream, ThrowingFunction mapper) throws X { return stream.flatMap(sneaky(mapper).andThen(Optional::stream)); } - public static , V, X extends Exception> Stream flatMapOptionalIgnoreEx( + public static , V extends @Nullable Object, + X extends Exception> Stream flatMapOptionalIgnoreEx( @This Stream stream, ThrowingFunction mapper) { try { return stream.flatMapOptionalEx(mapper); @@ -59,13 +103,101 @@ public static , V, X extends Exception> Stream flatM } } - public static void forEachEx(@This Stream stream, ThrowingConsumer consumer) - throws X { + // MAP TO INT + + public static IntStream mapToIntEx(@This Stream stream, + ThrowingToIntFunction mapper) throws X { + return stream.mapToInt(sneaky(mapper)); + } + + public static IntStream mapToIntIgnoreEx(@This Stream stream, + ThrowingToIntFunction mapper) throws X { + try { + return stream.mapToIntEx(mapper); + } catch (Exception e) { + return IntStream.empty(); + } + } + + + // MAP TO LONG + + public static LongStream mapToLongEx(@This Stream stream, + ThrowingToLongFunction mapper) throws X { + return stream.mapToLong(sneaky(mapper)); + } + + public static LongStream mapToLongIgnoreEx(@This Stream stream, + ThrowingToLongFunction mapper) throws X { + try { + return stream.mapToLongEx(mapper); + } catch (Exception e) { + return LongStream.empty(); + } + } + + // MAP TO DOUBLE + + public static DoubleStream mapToDoubleEx(@This Stream stream, + ThrowingToDoubleFunction mapper) throws X { + return stream.mapToDouble(sneaky(mapper)); + } + + public static DoubleStream mapToDoubleIgnoreEx( + @This Stream stream, ThrowingToDoubleFunction mapper) throws X { + try { + return stream.mapToDoubleEx(mapper); + } catch (Exception e) { + return DoubleStream.empty(); + } + } + + // FOR EACH // + + public static void forEachEx(@This Stream stream, + ThrowingConsumer consumer) throws X { stream.forEach(sneaky(consumer)); } - public static Stream filterCast(@This Stream stream, Class type) { + public static Stream filterCast(@This Stream stream, Class type) { return stream.filter(type::isInstance).map(type::cast); } -} + + public static Stream forEachContinue(@This Stream stream, Consumer consumer) { + return stream.map(e -> { + consumer.accept(e); + return e; + }); + } + + public static Stream forEachContinueEx(@This Stream stream, + ThrowingConsumer consumer) throws X { + return forEachContinue(stream, sneaky(consumer)); + } + + // FILTER // + + + public static Stream filterEx(@This Stream stream, + ThrowingPredicate predicate) throws X { + return stream.filter(sneaky(predicate)); + } + + + public static Stream filterGet(@This Stream> stream) { + return stream.filter(Optional::isPresent).map(Optional::get); + } + + public static @Self Stream exclude(@This Stream stream, Predicate filter) { + return stream.filter(not(filter)); + } + + @SuppressWarnings("SimplifyStreamApiCallChains") + public static @Self Stream use(@This Stream stream, Consumer consumer) { + return stream.map(e -> { + consumer.accept(e); + return e; + }); + } +} \ No newline at end of file diff --git a/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java b/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java index fd8101f4..97e50132 100644 --- a/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java +++ b/SubLibrary/src/main/java/extensions/manifold/rt/api/Array/ArrayExt.java @@ -7,8 +7,10 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.Self; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class ArrayExt { private ArrayExt() { diff --git a/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java index c65db7ed..4225e3f9 100644 --- a/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java +++ b/SubLibrary/src/main/java/extensions/manifold/science/measures/Time/TimeExt.java @@ -7,8 +7,10 @@ import manifold.science.measures.Time; import manifold.science.measures.TimeUnit; import manifold.science.util.Rational; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class TimeExt { public static boolean isPositive(@This Time time) { diff --git a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java index 7da4c811..2299110a 100644 --- a/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java +++ b/SubLibrary/src/main/java/extensions/org/codehaus/plexus/components/interactivity/Prompter/PrompterExt.java @@ -3,6 +3,7 @@ import static com.pivovarit.gatherers.MoreGatherers.*; import static java.lang.System.*; import static org.lodder.subtools.multisubdownloader.Messages.*; +import static util.Utils.*; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -18,16 +19,17 @@ import java.util.stream.Collectors; import dnl.utils.text.table.TextTable; -import manifold.ext.rt.api.This; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.function.TriFunction; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.Validator; // see #692 //@Extension +@NullMarked public class PrompterExt { private PrompterExt() { @@ -44,7 +46,7 @@ private PrompterExt() { getText("Prompter.NoAbbreviation").equalsIgnoreCase(v) || getText("Prompter.No").equalsIgnoreCase(v)); - public static void show(@This Prompter prompter, String message, Object... replacements) { + public static void show(Prompter prompter, String message, Object... replacements) { try { prompter.showMessage(message.formatted(replacements)); } catch (PrompterException e) { @@ -52,32 +54,36 @@ public static void show(@This Prompter prompter, String message, Object... repla } } - public static void pressAnyKeyToContinue(@This Prompter prompter) { - prompt(prompter:prompter, message:"Press any key to continue", toObjectMapper:Function.identity()); + public static void pressAnyKeyToContinue(Prompter prompter) { + prompt(prompter, "Press any key to continue", List.of(), Function.identity()); } - public static Optional promptString(@This Prompter prompter, String message, - List> inputValidators=List.of(NON_BLANK_VALIDATOR), - List> objectValidators=new ArrayList>()) { + public static Optional promptString(Prompter prompter, String message, + @Nullable List> inputValidators=List.of(NON_BLANK_VALIDATOR), + @Nullable List> objectValidators=new ArrayList>()) { - return prompt(prompter, message, inputValidators, Function.identity(), objectValidators); + return prompt(prompter, message, ifNullThenGet(inputValidators, () -> List.of(NON_BLANK_VALIDATOR)), + Function.identity(), ifNullThenGet(objectValidators, List::of)); } - public static OptionalInt promptInt(@This Prompter prompter, String message, - List> inputValidators=List.of(NON_BLANK_VALIDATOR, INT_VALIDATOR), - Function toObjectMapper=Integer::parseInt, - List> objectValidators=new ArrayList>()) { + public static OptionalInt promptInt(Prompter prompter, String message, + @Nullable List> inputValidators=List.of(NON_BLANK_VALIDATOR, INT_VALIDATOR), + @Nullable Function toObjectMapper=Integer::parseInt, + @Nullable List> objectValidators=new ArrayList>()) { - return prompt(prompter, message, inputValidators, toObjectMapper, objectValidators).mapToIntEx(v -> v); + return prompt(prompter, message, + ifNullThenGet(inputValidators, () -> List.of(NON_BLANK_VALIDATOR, INT_VALIDATOR)), + toObjectMapper != null ? toObjectMapper : Integer::parseInt, + ifNullThenGet(objectValidators, List::of)).mapToIntEx(v -> v); } - public static Optional promptBoolean(@This Prompter prompter, String message, + public static Optional promptBoolean(Prompter prompter, String message, List> inputValidators=List.of(NON_BLANK_VALIDATOR, BOOLEAN_VALIDATOR)) { return prompt(prompter, message, inputValidators, Boolean::parseBoolean, List.of()); } - public static Optional promptValueFromList(@This Prompter prompter, + public static Optional promptValueFromList(Prompter prompter, String message, Iterable elements, Function toStringMapper=String::valueOf, @@ -85,12 +91,13 @@ public static Optional promptValueFromList(@This Prompter prompter, @Nullable TableDisplayer tableDisplayer=null, @Nullable Comparator sorter=null) { - Validator inputValidator = - new Validator<>(v -> v == null || v.parseAsNumber(Integer::parseUnsignedInt).isPresent()); - Function toObjectMapper = v -> v == null ? null : Integer.parseUnsignedInt(v); + Validator<@Nullable String> inputValidator = + new Validator<@Nullable String>(v -> v == null || v.parseAsNumber(Integer::parseUnsignedInt).isPresent()); + Function<@Nullable String, @Nullable Integer> toObjectMapper = + v -> v == null ? null : Integer.parseUnsignedInt(v); int numberOfElements = elements.size(); - List> objectValidators = - List.of(new Validator<>(number -> number == null || (number > 0 && number <= numberOfElements), + List> objectValidators = List.of( + new Validator<@Nullable Integer>(number -> number == null || (number > 0 && number <= numberOfElements), getText("Prompter.ValueNotInRange", numberOfElements))); TriFunction>, List, Optional> promptFunction = (choicesMessage, @@ -103,7 +110,7 @@ public static Optional promptValueFromList(@This Prompter prompter, List.of(inputValidator), promptFunction); } - public static List promptValuesFromList(@This Prompter prompter, + public static List promptValuesFromList(Prompter prompter, String message, Iterable elements, Function toStringMapper=String::valueOf, @@ -111,7 +118,7 @@ public static List promptValuesFromList(@This Prompter prompter, @Nullable TableDisplayer tableDisplayer=null, @Nullable Comparator sorter=null) { - Validator inputValidator = new Validator<>(v -> v == null || + Validator<@Nullable String> inputValidator = new Validator<@Nullable String>(v -> v == null || v.split(",").stream().allMatch(n -> n.parseAsNumber(Integer::parseUnsignedInt).isPresent())); Function toObjectsMapper = v -> Arrays.stream(v.split(",")).mapToInt(Integer::parseUnsignedInt).toArray(); @@ -126,8 +133,7 @@ public static List promptValuesFromList(@This Prompter prompter, inputValidators, sortedElements) -> // TODO use extension method PrompterExt.promptValues(prompter, choicesMessage, inputValidators, toObjectsMapper, objectValidators) - .stream() - .map(idx -> sortedElements.get(idx - 1)).toList(); + .stream().map(idx -> sortedElements.get(idx - 1)).toList(); return promptFromList(message, elements, toStringMapper, includeNull, tableDisplayer, sorter, List.of(inputValidator), promptFunction); @@ -167,8 +173,8 @@ private static R promptFromList( private static Optional prompt(Prompter prompter, String message, List> inputValidators=new ArrayList>(), - Function toObjectMapper, - List> objectValidators=new ArrayList>()) { + Function toObjectMapper, + List> objectValidators=new ArrayList>()) { try { String value = prompter.prompt(message + System.lineSeparator()); for (Validator inputValidator : inputValidators) { @@ -178,7 +184,7 @@ private static Optional prompt(Prompter prompter, String message, } } T object = toObjectMapper.apply(value); - for (Validator objectValidator : objectValidators) { + for (Validator<@Nullable T> objectValidator : objectValidators) { if (objectValidator.isInvalid(object)) { prompter.showMessage(objectValidator.errorMessage); return prompt(prompter, message, inputValidators, toObjectMapper, objectValidators); @@ -190,7 +196,7 @@ private static Optional prompt(Prompter prompter, String message, } } - public static T promptValues(@This Prompter prompter, String message, + public static T promptValues(Prompter prompter, String message, List> inputValidators=new ArrayList>(), Function toObjectsMapper, List> objectValidators=new ArrayList>()) { @@ -219,6 +225,7 @@ public static T promptValues(@This Prompter prompter, String message, } } + @NullMarked public static class TableDisplayer { private final List> columnDisplayers; @@ -268,6 +275,7 @@ private void writeToPrintStream(Iterable tableElements, PrintStream printStre } } + @NullMarked private static class LineReadingOutputStream extends OutputStream { private final ByteArrayOutputStream byteArrayOutputStream; @@ -288,6 +296,7 @@ public String toString() { } + @NullMarked public record ColumnDisplayer(String columnName, Function toStringMapper) { } } diff --git a/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java b/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java index 2359f47f..794ab94b 100644 --- a/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java +++ b/SubLibrary/src/main/java/extensions/org/json/JSONArray/JSONArrayExt.java @@ -7,8 +7,10 @@ import manifold.ext.rt.api.This; import org.json.JSONArray; import org.json.JSONObject; +import org.jspecify.annotations.NullMarked; @Extension +@NullMarked public class JSONArrayExt { private JSONArrayExt() { diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java index 300fa08e..775c885f 100644 --- a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/ElementExt.java @@ -10,10 +10,12 @@ import org.jsoup.select.Elements; import org.jsoup.select.Evaluator; import org.jsoup.select.QueryParser; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.exception.WebpageException; @Extension +@NullMarked public class ElementExt { // --------------- \\ diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java index 25c98615..5e7d9dc8 100644 --- a/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java +++ b/SubLibrary/src/main/java/extensions/org/jsoup/nodes/Element/NthElementFinder.java @@ -7,12 +7,14 @@ import org.jsoup.select.Evaluator; import org.jsoup.select.NodeFilter; import org.jsoup.select.NodeTraversor; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +@NullMarked class NthElementFinder implements NodeFilter { private final Evaluator eval; private Element evalRoot; - private Element match; + private @Nullable Element match; private int index; private int currentIdx; diff --git a/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java index 24845dac..5bbe925b 100644 --- a/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java +++ b/SubLibrary/src/main/java/extensions/org/jsoup/select/Elements/UnmodifiableElements.java @@ -34,51 +34,63 @@ public UnmodifiableElements(Element... elements) { super(Arrays.asList(elements)); } + @Override public Elements removeAttr(String attributeKey) { return this; } + @Override public Elements addClass(String className) { return this; } + @Override public Elements removeClass(String className) { return this; } + @Override public Elements toggleClass(String className) { return this; } + @Override public Elements prepend(String html) { return this; } + @Override public Elements append(String html) { return this; } + @Override public Elements before(String html) { return this; } + @Override public Elements after(String html) { return this; } + @Override public Elements wrap(String html) { return this; } + @Override public Elements unwrap() { return this; } + @Override public Elements empty() { return this; } + @Override public Elements remove() { return this; } diff --git a/SubLibrary/src/main/java/extensions/org/slf4j/Logger/LoggerExt.java b/SubLibrary/src/main/java/extensions/org/slf4j/Logger/LoggerExt.java index bc5f50b2..dec8cf1c 100644 --- a/SubLibrary/src/main/java/extensions/org/slf4j/Logger/LoggerExt.java +++ b/SubLibrary/src/main/java/extensions/org/slf4j/Logger/LoggerExt.java @@ -2,10 +2,12 @@ import manifold.ext.rt.api.Extension; import manifold.ext.rt.api.This; +import org.jspecify.annotations.NullMarked; import org.lodder.subtools.sublibrary.LogLevel; import org.slf4j.Logger; @Extension +@NullMarked public class LoggerExt { public static void log(@This Logger logger, LogLevel logLevel, String message, Object arg){ diff --git a/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java b/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java index b36d5d40..22aa3896 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/multisubdownloader/Messages.java @@ -7,7 +7,6 @@ import manifold.ext.props.rt.api.var; import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.Language; @NullMarked @@ -19,7 +18,7 @@ public class Messages { private Messages() { } - public static String getText(String key, @Nullable Object... replacements) { + public static String getText(String key, Object... replacements) { try { String text = resourceBundle.getString(key); return replacements.isEmpty() ? text : text.formatted(replacements); @@ -28,7 +27,7 @@ public static String getText(String key, @Nullable Object... replacements) { } } - public static String getText(String key, Language language, @Nullable Object... replacements) { + public static String getText(String key, Language language, Object... replacements) { try { String text = getMessageBundle(language).getString(key); return replacements.isEmpty() ? text : text.formatted(replacements); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheStrategy.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheStrategy.java index 0ed735f8..be9fb5d3 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheStrategy.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/CacheStrategy.java @@ -1,5 +1,8 @@ package org.lodder.subtools.sublibrary; +import org.jspecify.annotations.NullMarked; + +@NullMarked public enum CacheStrategy { CACHE_DISABLED, CACHE_TEMPORARY, diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java index 80b08194..bccb2892 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/ConfigProperties.java @@ -5,9 +5,11 @@ import java.util.Properties; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public final class ConfigProperties { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigProperties.class); @@ -27,6 +29,7 @@ public static String getProperty(Property property) { return instance.prop.getProperty(property.value); } + @NullMarked public enum Property { NAME("name"), VERSION("version"); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java index 32e43b58..a758043d 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Credentials.java @@ -1,4 +1,7 @@ package org.lodder.subtools.sublibrary; +import org.jspecify.annotations.NullMarked; + +@NullMarked public record Credentials(String username, String password) { } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java index 11c4f4ea..b37d9189 100755 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/DetectLanguage.java @@ -5,7 +5,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Objects; import java.util.Optional; import com.optimaize.langdetect.LanguageDetector; @@ -14,11 +13,14 @@ import com.optimaize.langdetect.profiles.LanguageProfileReader; import com.optimaize.langdetect.text.CommonTextObjectFactories; import com.optimaize.langdetect.text.TextObjectFactory; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.util.lazy.LazySupplier; import org.lodder.subtools.sublibrary.util.lazy.LazyThrowingSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@NullMarked public class DetectLanguage { private static final Logger LOGGER = LoggerFactory.getLogger(DetectLanguage.class); @@ -31,11 +33,11 @@ public class DetectLanguage { new LazySupplier<>(CommonTextObjectFactories::forDetectingOnLargeText); private static final double MIN_PROBABILITY = 0.9; - public static Language execute(Path file) { + public static @Nullable Language execute(Path file) { return execute(file, null); } - public static Language execute(Path file, Language defaultLang) { + public static @Nullable Language execute(Path file, @Nullable Language defaultLang) { return executeOptional(file).orElse(defaultLang); } @@ -47,7 +49,7 @@ public static Optional executeOptional(Path file) { .filter(lang -> lang.getProbability() >= MIN_PROBABILITY) .findFirst() .map(lang -> lang.getLocale().getLanguage()) - .map(Language::ofIso639_3).filter(Objects::nonNull); + .mapNullable(Language::ofIso639_3); } catch (IOException e) { LOGGER.error("Could not detect language of file {} ", file); return Optional.empty(); diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java index c364d080..45e89d89 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Language.java @@ -1,9 +1,11 @@ package org.lodder.subtools.sublibrary; import manifold.ext.props.rt.api.val; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.multisubdownloader.Messages; +@NullMarked public enum Language { ABKHAZIAN("App.Language.Abkhazian", "abk", "ab"), diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/LogLevel.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/LogLevel.java index 1b90ebab..e5bb91c2 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/LogLevel.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/LogLevel.java @@ -1,5 +1,8 @@ package org.lodder.subtools.sublibrary; +import org.jspecify.annotations.NullMarked; + +@NullMarked public enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR } diff --git a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java index 6ab59da9..f21ce999 100644 --- a/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java +++ b/SubLibrary/src/main/java/org/lodder/subtools/sublibrary/Manager.java @@ -3,7 +3,6 @@ import static manifold.science.measures.TimeUnit.*; import static manifold.science.util.UnitConstants.*; -import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -32,6 +31,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.jsoup.Jsoup; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.lodder.subtools.sublibrary.cache.CacheType; import org.lodder.subtools.sublibrary.cache.ProviderCache; @@ -51,6 +51,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; +@NullMarked public class Manager { private static final Logger LOGGER = LoggerFactory.getLogger(Manager.class); @@ -66,7 +67,7 @@ public Manager(HttpClient httpClient, ProviderCacheMemory inMemoryCache, Prov } public void downloadAndExtractFile(String downloadLink, Path file, - ThrowingConsumer validateFunction=null) throws IOException { + @Nullable ThrowingConsumer validateFunction=null) throws IOException { try { httpClient.downloadAndExtractFile(new URI(downloadLink).toURL(), file, validateFunction); if (!Files.exists(file)) { @@ -93,6 +94,7 @@ public PostBuilder postBuilder(String url, @Nullable String userAgent=null) { return new PostBuilder(httpClient, url, userAgent); } + @NullMarked public static class PostBuilder { @val HttpClient httpClient; @val String url; @@ -123,7 +125,6 @@ public String post() throws ManagerException { public org.jsoup.nodes.Document postAsJsoupDocument() throws ManagerException { return Jsoup.parse(post()); } - } // ================ \\ @@ -146,12 +147,11 @@ public InputStream getAsInputStream(PageContentParams params) throws ManagerExce public @Nullable Document getAsDocument(PageContentParams params, @Nullable Predicate emptyResultPredicate=null) - throws ParserConfigurationException, ManagerException, IOException { - Optional asStringDocument = getAsStringDocument(params, emptyResultPredicate); - return asStringDocument.isPresent() ? XMLHelper.getDocument(asStringDocument.get()) : null; + throws ManagerException, IOException { + return getAsStringDocument(params, emptyResultPredicate).mapEx(XMLHelper::getDocument).orElse(null); } - public org.jsoup.nodes.Document getAsJsoupDocument(PageContentParams params, + public org.jsoup.nodes.@Nullable Document getAsJsoupDocument(PageContentParams params, @Nullable Predicate emptyResultPredicate=null) throws ManagerException { return getAsStringDocument(params, emptyResultPredicate).map(Jsoup::parse).orElse(null); } @@ -186,7 +186,7 @@ private String getContentWithoutCache(PageContentParams params) throws ManagerEx } private String getContentWithoutCache(String url, String userAgent, Retry retry, - CookieManager cookieManager) throws ManagerException { + @Nullable CookieManager cookieManager) throws ManagerException { try { return httpClient.doGet(new URI(url).toURL(), userAgent, cookieManager); } catch (HttpClientException e) { @@ -206,9 +206,10 @@ private String getContentWithoutCache(String url, String userAgent, Retry retry, } } + @NullMarked public record Retry(int retries, Predicate predicate, Time waitTime) { - public static final Retry NONE = new Retry(0, null, 0Second); + public static final Retry NONE = new Retry(0, _ -> false, 0Second); public Retry { if (retries < 0) { @@ -231,7 +232,7 @@ public Retry sleep() { } public List> getEntries(CacheType cacheType, - @Nullable Predicate keyFilter, Class type=null) { + Predicate keyFilter, @Nullable Class type=null) { return getCache(cacheType, keyFilter).getEntries(type); } @@ -240,6 +241,7 @@ public List> getEntries(CacheType cacheType, // CACHE METHODS \\ // ============= \\ + @NullMarked public record CacheKey(Manager manager, CacheType cacheType, ProviderCacheKey key) { public boolean isPresent() { @@ -260,15 +262,15 @@ public boolean isTemporaryObject() { public Optional
+ + org.htmlunit + htmlunit + 4.16.0 + + + org.jetbrains + annotations + 26.0.2-1 + org.mockito mockito-core - 5.16.1 + 5.21.0 test