⬆️ Use JSpecify for nullability annotations
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 6 Oct 2025 15:21:32 +0000 (17:21 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 6 Oct 2025 15:21:32 +0000 (17:21 +0200)
24 files changed:
build.gradle
src/main/java/net/pterodactylus/rhynodge/Filter.java
src/main/java/net/pterodactylus/rhynodge/State.java
src/main/java/net/pterodactylus/rhynodge/filters/BlacklistFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/ComicSiteFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/EpisodeFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/ExtractUrlFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/HtmlFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/HttpQueryFilter.java
src/main/java/net/pterodactylus/rhynodge/filters/TorrentSiteFilter.java
src/main/java/net/pterodactylus/rhynodge/mergers/ComicMerger.java
src/main/java/net/pterodactylus/rhynodge/mergers/EpisodeMerger.java
src/main/java/net/pterodactylus/rhynodge/mergers/TorrentMerger.java
src/main/java/net/pterodactylus/rhynodge/states/AbstractState.java
src/main/java/net/pterodactylus/rhynodge/states/ComicState.java
src/main/java/net/pterodactylus/rhynodge/states/EpisodeState.java
src/main/java/net/pterodactylus/rhynodge/states/FailedState.java
src/main/java/net/pterodactylus/rhynodge/states/FileState.java
src/main/java/net/pterodactylus/rhynodge/states/HtmlState.java
src/main/java/net/pterodactylus/rhynodge/states/HttpState.java
src/main/java/net/pterodactylus/rhynodge/states/OutputState.java
src/main/java/net/pterodactylus/rhynodge/states/StringState.java
src/main/java/net/pterodactylus/rhynodge/states/TorrentState.java
src/test/java/net/pterodactylus/rhynodge/states/StateManagerTest.java

index bd7bd42..cc1705f 100644 (file)
@@ -56,7 +56,7 @@ dependencies {
     implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.16.1'
     implementation group: "com.google.inject", name: "guice", version: "7.0.0"
     implementation group: "org.jetbrains.kotlinx", name: "kotlinx-html-jvm", version: "0.11.0"
-    implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
+    implementation group: 'org.jspecify', name: 'jspecify', version: '1.0.0'
 
     testImplementation(platform("org.junit:junit-bom:5.11.3"))
     testImplementation group: "org.junit.jupiter", name: "junit-jupiter-engine"
index 3095623..d1ac741 100644 (file)
@@ -17,7 +17,8 @@
 
 package net.pterodactylus.rhynodge;
 
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Defines a filter that runs between {@link Query}s and {@link Merger}s and
@@ -40,7 +41,7 @@ public interface Filter {
         *            The state to convert
         * @return The new state
         */
-       @NotNull
-       State filter(@NotNull State state);
+       @NonNull
+       State filter(@Nullable State state);
 
 }
index 966dbe3..aa755be 100644 (file)
@@ -17,9 +17,8 @@
 
 package net.pterodactylus.rhynodge;
 
-import javax.annotation.Nonnull;
-
 import net.pterodactylus.rhynodge.output.Output;
+import org.jspecify.annotations.NonNull;
 
 /**
  * Defines the current state of a system.
@@ -95,7 +94,7 @@ public interface State {
         */
        Throwable exception();
 
-       @Nonnull
+       @NonNull
        Output output(Reaction reaction);
 
 }
index 5bac2a0..7c29a08 100644 (file)
@@ -30,7 +30,7 @@ import net.pterodactylus.rhynodge.states.TorrentState;
 import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
 
 import com.google.common.base.Predicate;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NonNull;
 
 /**
  * Filter for {@link TorrentState}s that removes all {@link TorrentFile}s whose
@@ -46,9 +46,9 @@ public class BlacklistFilter implements Filter {
                this.filterWords = filterWords;
        }
 
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                if (!state.success()) {
                        return FailedState.from(state);
                }
index 6bc67cd..e8ea1c1 100644 (file)
@@ -33,8 +33,8 @@ import net.pterodactylus.rhynodge.states.FailedState;
 import net.pterodactylus.rhynodge.states.HtmlState;
 
 import com.google.common.base.Optional;
-import org.jetbrains.annotations.NotNull;
 import org.jsoup.nodes.Document;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} implementation that can extract {@link ComicState}s from
@@ -44,9 +44,9 @@ import org.jsoup.nodes.Document;
  */
 public abstract class ComicSiteFilter implements Filter {
 
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                checkArgument(state instanceof HtmlState, "state must be an HTML state");
 
                /* initialize states: */
index 6aa3e1f..eb34795 100644 (file)
@@ -39,7 +39,7 @@ import com.google.common.base.Optional;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import org.apache.log4j.Logger;
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} implementation that extracts {@link Episode} information from
@@ -61,9 +61,9 @@ public class EpisodeFilter implements Filter {
        /**
         * {@inheritDoc}
         */
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                if (!state.success()) {
                        return FailedState.from(state);
                }
index 78c589e..38add86 100644 (file)
@@ -26,8 +26,8 @@ import net.pterodactylus.rhynodge.states.HtmlState;
 import net.pterodactylus.rhynodge.states.StringState;
 
 import com.google.common.base.Optional;
-import org.jetbrains.annotations.NotNull;
 import org.jsoup.nodes.Document;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} implementation that extracts a URL from an {@link HtmlState}.
@@ -36,9 +36,9 @@ import org.jsoup.nodes.Document;
  */
 public abstract class ExtractUrlFilter implements Filter {
 
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                checkArgument(state instanceof HtmlState, "state must be an HTML state");
 
                HtmlState htmlState = (HtmlState) state;
index 788203c..4e4d02f 100644 (file)
@@ -26,9 +26,9 @@ import net.pterodactylus.rhynodge.states.HtmlState;
 import net.pterodactylus.rhynodge.states.HttpState;
 
 import org.apache.log4j.Logger;
-import org.jetbrains.annotations.NotNull;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} that converts a {@link HttpState} into an {@link HtmlState}.
@@ -42,9 +42,9 @@ public class HtmlFilter implements Filter {
        /**
         * {@inheritDoc}
         */
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                if (!state.success()) {
                        return FailedState.from(state);
                }
index 00533de..2ba004e 100644 (file)
@@ -25,7 +25,7 @@ import net.pterodactylus.rhynodge.queries.HttpQuery;
 import net.pterodactylus.rhynodge.states.HttpState;
 import net.pterodactylus.rhynodge.states.StringState;
 
-import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} implementation that uses the {@link StringState#value() value}
@@ -36,9 +36,9 @@ import org.jetbrains.annotations.NotNull;
  */
 public class HttpQueryFilter implements Filter {
 
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                checkArgument(state instanceof StringState, "state must be a String state");
 
                StringState stringState = (StringState) state;
index cf1a8b5..21f3fd6 100644 (file)
@@ -32,10 +32,10 @@ import net.pterodactylus.rhynodge.states.HtmlState;
 import net.pterodactylus.rhynodge.states.TorrentState;
 import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
 
-import org.jetbrains.annotations.NotNull;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link Filter} implementation that parses a {@link TorrentState} from an
@@ -49,9 +49,9 @@ public abstract class TorrentSiteFilter implements Filter {
        /**
         * {@inheritDoc}
         */
-       @NotNull
+       @NonNull
        @Override
-       public State filter(@NotNull State state) {
+       public State filter(@NonNull State state) {
                if (!state.success()) {
                        return FailedState.from(state);
                }
index 5b8f8df..68c7122 100644 (file)
@@ -21,12 +21,12 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import javax.annotation.Nonnull;
 
 import net.pterodactylus.rhynodge.Merger;
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.states.ComicState;
 import net.pterodactylus.rhynodge.states.ComicState.Comic;
+import org.jspecify.annotations.NonNull;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -37,9 +37,9 @@ import static com.google.common.base.Preconditions.checkArgument;
  */
 public class ComicMerger implements Merger {
 
-       @Nonnull
+       @NonNull
        @Override
-       public State mergeStates(@Nonnull State previousState, @Nonnull State currentState) {
+       public State mergeStates(@NonNull State previousState, @NonNull State currentState) {
                checkArgument(previousState instanceof ComicState, "previous state must be a comic state");
                checkArgument(currentState instanceof ComicState, "current state must be a comic state");
 
index 9101c36..97712f6 100644 (file)
@@ -22,13 +22,12 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.Map;
 
-import javax.annotation.Nonnull;
-
 import net.pterodactylus.rhynodge.Merger;
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.states.EpisodeState;
 import net.pterodactylus.rhynodge.states.EpisodeState.Episode;
 import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
+import org.jspecify.annotations.NonNull;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.function.Function.identity;
@@ -44,9 +43,9 @@ public class EpisodeMerger implements Merger {
        /**
         * {@inheritDoc}
         */
-       @Nonnull
+       @NonNull
        @Override
-       public State mergeStates(@Nonnull State previousState, @Nonnull State currentState) {
+       public State mergeStates(@NonNull State previousState, @NonNull State currentState) {
                checkState(currentState instanceof EpisodeState, "currentState is not a EpisodeState but a %s", currentState.getClass().getName());
                checkState(previousState instanceof EpisodeState, "previousState is not a EpisodeState but a %s", currentState.getClass().getName());
 
index f6a75a8..fcfbd81 100644 (file)
@@ -19,12 +19,12 @@ package net.pterodactylus.rhynodge.mergers;
 
 import java.util.HashSet;
 import java.util.Set;
-import javax.annotation.Nonnull;
 
 import net.pterodactylus.rhynodge.Merger;
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.states.TorrentState;
 import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
+import org.jspecify.annotations.NonNull;
 
 import static com.google.common.base.Preconditions.checkState;
 
@@ -40,9 +40,9 @@ public class TorrentMerger implements Merger {
        /**
         * {@inheritDoc}
         */
-       @Nonnull
+       @NonNull
        @Override
-       public State mergeStates(@Nonnull State previousState, @Nonnull State currentState) {
+       public State mergeStates(@NonNull State previousState, @NonNull State currentState) {
                checkState(currentState instanceof TorrentState, "currentState is not a TorrentState but a %s", currentState.getClass().getName());
                checkState(previousState instanceof TorrentState, "previousState is not a TorrentState but a %s", currentState.getClass().getName());
 
index ecd520f..f925f8c 100644 (file)
@@ -19,8 +19,6 @@ package net.pterodactylus.rhynodge.states;
 
 import java.time.Clock;
 import java.util.Objects;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import net.pterodactylus.rhynodge.Reaction;
 import net.pterodactylus.rhynodge.State;
@@ -31,6 +29,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.google.common.escape.Escaper;
 import com.google.common.html.HtmlEscapers;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Abstract implementation of a {@link State} that knows about the basic
@@ -202,7 +202,7 @@ public abstract class AbstractState implements State {
                return exception;
        }
 
-       @Nonnull
+       @NonNull
        @Override
        public Output output(Reaction reaction) {
                return new DefaultOutput(summary(reaction))
@@ -210,12 +210,12 @@ public abstract class AbstractState implements State {
                                .addText("text/html", htmlText());
        }
 
-       @Nonnull
+       @NonNull
        protected String summary(Reaction reaction) {
                return reaction.name();
        }
 
-       @Nonnull
+       @NonNull
        protected abstract String plainText();
 
        @Nullable
index 0e661bc..ec531be 100644 (file)
@@ -24,8 +24,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import net.pterodactylus.rhynodge.Reaction;
 import net.pterodactylus.rhynodge.states.ComicState.Comic;
@@ -34,6 +32,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.collect.Lists;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 import static java.lang.String.format;
 
@@ -87,13 +87,13 @@ public class ComicState extends AbstractState implements Iterable<Comic> {
                return format("ComicState[comics=%s]", comics());
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String summary(Reaction reaction) {
                return format("New Comic found for “%s!”", reaction.name());
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                StringBuilder text = new StringBuilder();
index abb387d..3ac4e1a 100644 (file)
@@ -25,8 +25,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import net.pterodactylus.rhynodge.Reaction;
 import net.pterodactylus.rhynodge.State;
@@ -40,6 +38,8 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Ordering;
 import org.apache.commons.lang3.StringEscapeUtils;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * {@link State} implementation that stores episodes of TV shows, parsed via
@@ -104,7 +104,7 @@ public class EpisodeState extends AbstractState implements Iterable<Episode> {
                return Collections.unmodifiableCollection(episodes);
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String summary(Reaction reaction) {
                if (!newEpisodes.isEmpty()) {
@@ -116,7 +116,7 @@ public class EpisodeState extends AbstractState implements Iterable<Episode> {
                return String.format("%d changed Torrent(s) for “%s!”", changedEpisodes.size(), reaction.name());
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                StringBuilder stringBuilder = new StringBuilder();
index 6c1d844..e29e526 100644 (file)
@@ -21,9 +21,9 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.Writer;
-import javax.annotation.Nonnull;
 
 import net.pterodactylus.rhynodge.State;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link State} implementation that signals failure.
@@ -57,7 +57,7 @@ public class FailedState extends AbstractState {
                return true;
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                if (exception() == null) {
index 983500d..463278c 100644 (file)
@@ -17,9 +17,8 @@
 
 package net.pterodactylus.rhynodge.states;
 
-import javax.annotation.Nonnull;
-
 import net.pterodactylus.rhynodge.State;
+import org.jspecify.annotations.NonNull;
 
 /**
  * A {@link State} that contains information about a file.
@@ -121,7 +120,7 @@ public class FileState extends AbstractState {
                return modificationTime;
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                return toString();
index 83cd36d..644d5d0 100644 (file)
 
 package net.pterodactylus.rhynodge.states;
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
 import net.pterodactylus.rhynodge.State;
 
 import org.jsoup.nodes.Document;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * {@link State} implementation that contains a parsed HTML {@link Document}.
@@ -77,7 +76,7 @@ public class HtmlState extends AbstractState {
                return document;
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                //noinspection ConstantConditions
index 76dbc94..5e62d73 100644 (file)
@@ -21,14 +21,13 @@ import static java.util.Arrays.copyOf;
 
 import java.io.UnsupportedEncodingException;
 
-import javax.annotation.Nonnull;
-
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
 
 import org.apache.http.HeaderElement;
 import org.apache.http.NameValuePair;
 import org.apache.http.message.BasicHeaderValueParser;
+import org.jspecify.annotations.NonNull;
 
 /**
  * {@link State} that contains the results of an {@link HttpQuery}.
@@ -128,7 +127,7 @@ public class HttpState extends AbstractState {
                }
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                return content();
index 92cc44c..617ea27 100644 (file)
@@ -2,10 +2,9 @@ package net.pterodactylus.rhynodge.states;
 
 import java.util.Optional;
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
 import net.pterodactylus.rhynodge.State;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * {@link State} implementation that can expose itself as plain text and/or
@@ -32,7 +31,7 @@ public class OutputState extends AbstractState {
                return !plainTextOutput.isPresent() && !htmlOutput.isPresent();
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                return plainTextOutput.orElse("");
index ad989b3..be089db 100644 (file)
@@ -17,7 +17,7 @@
 
 package net.pterodactylus.rhynodge.states;
 
-import javax.annotation.Nonnull;
+import org.jspecify.annotations.NonNull;
 
 /**
  * A {@link net.pterodactylus.rhynodge.State} that stores a single {@link
@@ -54,7 +54,7 @@ public class StringState extends AbstractState {
                return value.isEmpty();
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                return value;
index 99cb9d1..2e22e54 100644 (file)
@@ -26,8 +26,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
-import javax.annotation.Nonnull;
-
 import net.pterodactylus.rhynodge.Reaction;
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.output.DefaultOutput;
@@ -40,8 +38,8 @@ import com.google.common.collect.Ordering;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 import static com.google.common.collect.Ordering.from;
 import static java.lang.String.format;
@@ -117,13 +115,13 @@ public class TorrentState extends AbstractState implements Iterable<TorrentFile>
                return this;
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String summary(Reaction reaction) {
                return format("Found %d new Torrent(s) for “%s!”", newTorrentFiles.size(), reaction.name());
        }
 
-       @Nonnull
+       @NonNull
        @Override
        protected String plainText() {
                StringBuilder plainText = new StringBuilder();
index 639c8f7..efdcb33 100644 (file)
@@ -3,7 +3,6 @@ package net.pterodactylus.rhynodge.states;
 import java.io.File;
 import java.io.IOException;
 import java.util.Optional;
-import javax.annotation.Nonnull;
 
 import net.pterodactylus.rhynodge.State;
 import net.pterodactylus.rhynodge.states.StateManager.StateDirectory;
@@ -11,6 +10,7 @@ import net.pterodactylus.rhynodge.states.StateManager.StateDirectory;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.io.Files;
 import org.hamcrest.Matchers;
+import org.jspecify.annotations.NonNull;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -93,7 +93,7 @@ public class StateManagerTest {
                        super(success);
                }
 
-               @Nonnull
+               @NonNull
                @Override
                protected String plainText() {
                        return "Test";
@@ -106,7 +106,7 @@ public class StateManagerTest {
                @JsonProperty
                private final Object someObject = new Object();
 
-               @Nonnull
+               @NonNull
                @Override
                protected String plainText() {
                        return "Invalid";