🐛 Fix broken change detection
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 25 Oct 2021 19:28:11 +0000 (21:28 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 25 Oct 2021 19:28:11 +0000 (21:28 +0200)
Wow, this was a mess. Two days after the last deploy with a working change
detection I changed some code around the triggers and broke the detection. I
didn’t realize until over a year later because I have never deployed the broken
version until I made some unrelated changes.

I never liked the particular solution around the triggers keeping state and not
being pure functions. So now there are no more triggers; instead, the merging
of the states (which was done by the triggers â€” great misnomer there) is now
done by a merger, and the state is responsible for exposing whether it contains
a noteworthy change. The Merger and the State for a particular thing (like a
comic, or a torrent file) have to work together hand in hand here but they also
needed to do that before and it’s not really like you would use an X-Merger
with a Y-State.

The resulting changes touch pretty much everything but due to the Watcher
“shortcut” of chain creation, all .json files that only reference a Watcher do
not need to be touched which should make transition rather seamless.

61 files changed:
README.md
src/main/java/net/pterodactylus/rhynodge/Action.java
src/main/java/net/pterodactylus/rhynodge/Filter.java
src/main/java/net/pterodactylus/rhynodge/Reaction.java
src/main/java/net/pterodactylus/rhynodge/State.java
src/main/java/net/pterodactylus/rhynodge/Trigger.java [deleted file]
src/main/java/net/pterodactylus/rhynodge/Watcher.java
src/main/java/net/pterodactylus/rhynodge/engine/ReactionRunner.java
src/main/java/net/pterodactylus/rhynodge/loader/Chain.java
src/main/java/net/pterodactylus/rhynodge/loader/ChainWatcher.java
src/main/java/net/pterodactylus/rhynodge/loader/ReactionLoader.java
src/main/java/net/pterodactylus/rhynodge/mergers/ComicMerger.java [new file with mode: 0644]
src/main/java/net/pterodactylus/rhynodge/mergers/EpisodeMerger.java [new file with mode: 0644]
src/main/java/net/pterodactylus/rhynodge/mergers/TorrentMerger.java [new file with mode: 0644]
src/main/java/net/pterodactylus/rhynodge/output/Output.java
src/main/java/net/pterodactylus/rhynodge/package-info.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/TorrentState.java
src/main/java/net/pterodactylus/rhynodge/triggers/AlwaysTrigger.java [deleted file]
src/main/java/net/pterodactylus/rhynodge/triggers/NewComicTrigger.java [deleted file]
src/main/java/net/pterodactylus/rhynodge/triggers/NewEpisodeTrigger.java [deleted file]
src/main/java/net/pterodactylus/rhynodge/triggers/NewTorrentTrigger.java [deleted file]
src/main/java/net/pterodactylus/rhynodge/watchers/AbstruseGooseWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/Collar6Watcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/CtrlAltDelWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/CyanideAndHappinessWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/DefaultWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/DrugsAndWiresWatcher.kt
src/main/java/net/pterodactylus/rhynodge/watchers/GeneralProtectionFaultWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/GirlGeniusWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/HeldentageWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/KevinAndKellWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/KickAssTorrentsEpisodeWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/KickAssTorrentsWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/LeastICouldDoWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/PirateBayEpisodeWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/PirateBayWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/PoorlyDrawnLinesWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/SaturdayMorningBreakfastCerealWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/SavoyTicketWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/ScandinaviaAndTheWorldWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/SinfestWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/TorrentHoundEpisodeWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/TorrentHoundWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/TorrentzEuEpisodeWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/TorrentzEuWatcher.java
src/main/java/net/pterodactylus/rhynodge/watchers/XkcdWatcher.java
src/main/kotlin/net/pterodactylus/rhynodge/Merger.kt [new file with mode: 0644]
src/main/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMerger.kt [new file with mode: 0644]
src/main/kotlin/net/pterodactylus/rhynodge/watchers/BusinessCatWatcher.kt
src/main/kotlin/net/pterodactylus/rhynodge/watchers/QuestionableContentWatcher.kt
src/main/kotlin/net/pterodactylus/rhynodge/watchers/SoggyCardboardWatcher.kt
src/main/kotlin/net/pterodactylus/rhynodge/watchers/TheMonsterUnderTheBedWatcher.kt
src/main/resources/chains/kickasstorrents-example.json
src/main/resources/chains/thepiratebay-example.json
src/test/java/net/pterodactylus/rhynodge/ReactionTest.java
src/test/kotlin/net/pterodactylus/rhynodge/mergers/ComicMergerTest.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMergerTest.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/rhynodge/triggers/AlwaysTriggerTest.kt [deleted file]
src/test/kotlin/net/pterodactylus/rhynodge/triggers/NewComicTriggerTest.kt [deleted file]

index 32ebc3c..460f783 100644 (file)
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ Rhynodge also periodically scans the chains directory to find changed or new cha
 
 ## Internal Concepts
 
-The core of Rhynodge comprises ``Reaction``s which in turn consist of ``Query``s, ``Filter``s, ``Trigger``s, and ``Action``s.
+The core of Rhynodge comprises ``Reaction``s which in turn consist of ``Query``s, ``Filter``s, ``Merger``s, and ``Action``s.
 
 ### Query
 
@@ -44,18 +44,18 @@ A filter is an optional component that turns a ``State`` into a different ``Stat
 
 The result of a ``Filter`` is a ``State``, again.
 
-### Trigger
+### Merger
 
-A trigger decides if, given the current state and the previous state, a noteworthy change has occured. It could calculate the difference between two file lists, or of two Facebook post lists.
+A merger merges two states into a new state; the new state is responsible for detecting whether a noteworthy change has occured.
 
-The result of a ``Trigger`` is an ``Output``.
+The result of a ``Merger`` is — once again — a ``State``.
 
 ### Watcher
 
-A watcher combines a query, a list of filters, and a trigger into a single unit which can be configured a lot easier than the separate components. For example, you could have a â€œTwitter” that finds new status updates for a username and that only needs to be configured with that username; the rest of the configuration is contained in the watcher.
+A watcher combines a query, a list of filters, and a merger into a single unit which can be configured a lot easier than the separate components. For example, you could have a â€œTwitter” that finds new status updates for a username and that only needs to be configured with that username; the rest of the configuration is contained in the watcher.
 
-A ``Watcher`` does not do any processing but instead offers a ``Query``, a list of ``Filter``s, and a ``Trigger``.
+A ``Watcher`` does not do any processing but instead offers a ``Query``, a list of ``Filter``s, and a ``Merger``.
 
 ### Action
 
-If a trigger found a change, the action is then executed. Again, an action can be almost anything: it can send an email, it can execute programs, print documents, initiate phone calls, take a picture from a webcam — anything you can program can be used an an action.
+If a noteworthy change has been detected, the action is then executed. Again, an action can be almost anything: it can send an email, it can execute programs, print documents, initiate phone calls, take a picture from a webcam — anything you can program can be used an an action.
index 4a7ed56..157f555 100644 (file)
@@ -20,8 +20,8 @@ package net.pterodactylus.rhynodge;
 import net.pterodactylus.rhynodge.output.Output;
 
 /**
- * An action is performed when a {@link Trigger} determines that two given
- * {@link State}s of a {@link Query} signify a change.
+ * An action is performed when a {@link State} has {@link State#triggered()}
+ * a noteworthy change.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
  */
index 1906afa..3095623 100644 (file)
@@ -20,7 +20,7 @@ package net.pterodactylus.rhynodge;
 import org.jetbrains.annotations.NotNull;
 
 /**
- * Defines a filter that runs between {@link Query}s and {@link Trigger}s and
+ * Defines a filter that runs between {@link Query}s and {@link Merger}s and
  * can be used to convert a {@link State} into another {@link State}. This can
  * be used to extract further information from a state.
  * <p>
index 69e2592..4b390ef 100644 (file)
@@ -23,7 +23,7 @@ import java.util.List;
 import com.google.common.collect.Lists;
 
 /**
- * A {@code Reaction} binds together {@link Query}s, {@link Trigger}s, and
+ * A {@code Reaction} binds together {@link Query}s, {@link Merger}s, and
  * {@link Action}s, and it stores the intermediary {@link State}s.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
@@ -39,8 +39,8 @@ public class Reaction {
        /** The filters to run. */
        private final List<Filter> filters = Lists.newArrayList();
 
-       /** The trigger to detect changes. */
-       private final Trigger trigger;
+       /** The merger merges old and new states. */
+       private final Merger merger;
 
        /** The action to perform. */
        private final Action action;
@@ -55,13 +55,13 @@ public class Reaction {
         *            The name of the reaction
         * @param query
         *            The query to run
-        * @param trigger
-        *            The trigger to detect changes
+        * @param merger
+        *            The merger to merge states
         * @param action
         *            The action to perform
         */
-       public Reaction(String name, Query query, Trigger trigger, Action action) {
-               this(name, query, Collections.<Filter> emptyList(), trigger, action);
+       public Reaction(String name, Query query, Merger merger, Action action) {
+               this(name, query, Collections.<Filter> emptyList(), merger, action);
        }
 
        /**
@@ -73,16 +73,16 @@ public class Reaction {
         *            The query to run
         * @param filters
         *            The filters to run
-        * @param trigger
-        *            The trigger to detect changes
+        * @param merger
+        *            The merger to merge states
         * @param action
         *            The action to perform
         */
-       public Reaction(String name, Query query, List<Filter> filters, Trigger trigger, Action action) {
+       public Reaction(String name, Query query, List<Filter> filters, Merger merger, Action action) {
                this.name = name;
                this.query = query;
                this.filters.addAll(filters);
-               this.trigger = trigger;
+               this.merger = merger;
                this.action = action;
        }
 
@@ -119,12 +119,12 @@ public class Reaction {
        }
 
        /**
-        * Returns the trigger to detect changes.
+        * Returns the merger to merge states.
         *
-        * @return The trigger to detect changes
+        * @return The merger to merge states
         */
-       public Trigger trigger() {
-               return trigger;
+       public Merger merger() {
+               return merger;
        }
 
        /**
index bda5bf5..2a8ba1c 100644 (file)
@@ -47,6 +47,18 @@ public interface State {
         */
        boolean success();
 
+       /**
+        * Returns whether this state triggers a change notification. This can
+        * only return {@code true} if this state is the result of a
+        * {@link Merger} merging two states.
+        *
+        * @return {@code true} if this state triggers a change notification,
+        * {@code false} otherwise
+        */
+       default boolean triggered() {
+               return false;
+       }
+
        boolean isEmpty();
 
        /**
diff --git a/src/main/java/net/pterodactylus/rhynodge/Trigger.java b/src/main/java/net/pterodactylus/rhynodge/Trigger.java
deleted file mode 100644 (file)
index 9f43a46..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Rhynodge - Trigger.java - Copyright ÂŠ 2013 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.rhynodge;
-
-import net.pterodactylus.rhynodge.states.FileState;
-
-/**
- * A trigger determines whether two different states actually warrant a change
- * trigger. For example, two {@link FileState}s might contain different file
- * sizes but a trigger might only care about whether the file appeared or
- * disappeared since the last check.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
- */
-public interface Trigger {
-
-       /**
-        * Merges the current state into the previous state, returning the merged
-        * state.
-        *
-        * @param previousState
-        *            The previous state of the system
-        * @param currentState
-        *            The current state of a system
-        * @return The new state, containing a meaningful merge between the previous
-        *         and the current state
-        */
-       State mergeStates(State previousState, State currentState);
-
-       /**
-        * Checks whether the states given to {@link #mergeStates(State, State)}
-        * warrant a change trigger.
-        *
-        * @return {@code true} if the states given to
-        *         {@link #mergeStates(State, State)} warrant a change trigger,
-        *         {@code false} otherwise
-        */
-       boolean triggers();
-
-}
index 2ded28f..4e63846 100644 (file)
@@ -21,7 +21,7 @@ import java.util.List;
 
 /**
  * A {@code Watcher} combines a {@link Query}, a number of {@link Filter}s, and
- * a {@link Trigger}, as these parts are closely related after all.
+ * a {@link Merger}, as these parts are closely related after all.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
  */
@@ -42,10 +42,10 @@ public interface Watcher {
        public List<Filter> filters();
 
        /**
-        * Returns the trigger of the watcher.
+        * Returns the merger of the watcher.
         *
-        * @return The trigger of the watcher
+        * @return The merger of the watcher
         */
-       public Trigger trigger();
+       public Merger merger();
 
 }
index a4006d8..0538a0f 100644 (file)
@@ -15,8 +15,8 @@ import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
 import net.pterodactylus.rhynodge.Reaction;
 import net.pterodactylus.rhynodge.State;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.actions.EmailAction;
+import net.pterodactylus.rhynodge.Merger;
 import net.pterodactylus.rhynodge.output.DefaultOutput;
 import net.pterodactylus.rhynodge.output.Output;
 import net.pterodactylus.rhynodge.states.FailedState;
@@ -25,7 +25,7 @@ import org.apache.log4j.Logger;
 
 /**
  * Runs a {@link Reaction}, starting with its {@link Query}, running the {@link
- * State} through its {@link Filter}s, and finally checking the {@link Trigger}
+ * State} through its {@link Filter}s, and finally checking the {@link Merger}
  * for whether an {@link Action} needs to be executed.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
@@ -59,10 +59,10 @@ public class ReactionRunner implements Runnable {
                        reactionState.saveState(state);
                        return;
                }
-               Trigger trigger = reaction.trigger();
-               State newState = trigger.mergeStates(lastSuccessfulState.get(), state);
+               Merger merger = reaction.merger();
+               State newState = merger.mergeStates(lastSuccessfulState.get(), state);
                reactionState.saveState(newState);
-               if (trigger.triggers()) {
+               if (newState.triggered()) {
                        logger.info(format("Trigger was hit for %s, executing action...", reaction.name()));
                        reaction.action().execute(newState.output(reaction));
                }
index ab81754..c4eba26 100644 (file)
@@ -177,11 +177,11 @@ public class Chain {
        @JsonProperty
        private List<Part> filters = new ArrayList<Part>();
 
-       /** The trigger of the chain. */
+       /** The merger of the chain. */
        @JsonProperty
-       private Part trigger;
+       private Part merger;
 
-       /** A combination of query, filters, and a trigger. */
+       /** A combination of query, filters, and a merger. */
        @JsonProperty
        private Part watcher;
 
@@ -230,12 +230,12 @@ public class Chain {
        }
 
        /**
-        * Returns the trigger of this chain.
+        * Returns the merger of this chain.
         *
-        * @return The trigger of this chain
+        * @return The merger of this chain
         */
-       public Part trigger() {
-               return trigger;
+       public Part merger() {
+               return merger;
        }
 
        /**
@@ -283,7 +283,7 @@ public class Chain {
                        for (Part filter : filters) {
                                hashCode ^= filter.hashCode();
                        }
-                       hashCode ^= trigger.hashCode();
+                       hashCode ^= merger.hashCode();
                }
                hashCode ^= action.hashCode();
                hashCode ^= updateInterval;
@@ -318,7 +318,7 @@ public class Chain {
                                        return false;
                                }
                        }
-                       if (!trigger.equals(chain.trigger)) {
+                       if (!merger.equals(chain.merger)) {
                                return false;
                        }
                }
index 69faf8b..f0b1215 100644 (file)
@@ -143,8 +143,8 @@ public class ChainWatcher extends AbstractExecutionThreadService {
                                                        logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
                                                }
                                        }
-                                       logger.debug(String.format(" Trigger: %s", chain.trigger().name()));
-                                       for (Parameter parameter : chain.trigger().parameters()) {
+                                       logger.debug(String.format(" Trigger: %s", chain.merger().name()));
+                                       for (Parameter parameter : chain.merger().parameters()) {
                                                logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
                                        }
                                }
index 10608d9..94009af 100644 (file)
@@ -27,10 +27,10 @@ import net.pterodactylus.rhynodge.Action;
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
 import net.pterodactylus.rhynodge.Reaction;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.loader.Chain.Parameter;
 import net.pterodactylus.rhynodge.loader.Chain.Part;
+import net.pterodactylus.rhynodge.Merger;
 
 /**
  * Creates {@link Reaction}s from {@link Chain}s.
@@ -68,7 +68,7 @@ public class ReactionLoader {
                        Watcher watcher = createObject(chain.watcher().name(), "net.pterodactylus.rhynodge.watchers", extractParameters(chain.watcher().parameters()));
 
                        /* create reaction. */
-                       reaction = new Reaction(chain.name(), watcher.query(), watcher.filters(), watcher.trigger(), action);
+                       reaction = new Reaction(chain.name(), watcher.query(), watcher.filters(), watcher.merger(), action);
 
                } else {
 
@@ -81,11 +81,11 @@ public class ReactionLoader {
                                filters.add(ReactionLoader.<Filter> createObject(filterPart.name(), "net.pterodactylus.rhynodge.filters", extractParameters(filterPart.parameters())));
                        }
 
-                       /* create trigger. */
-                       Trigger trigger = createObject(chain.trigger().name(), "net.pterodactylus.rhynodge.triggers", extractParameters(chain.trigger().parameters()));
+                       /* create merger. */
+                       Merger merger = createObject(chain.merger().name(), "net.pterodactylus.rhynodge.mergers", extractParameters(chain.merger().parameters()));
 
                        /* create reaction. */
-                       reaction = new Reaction(chain.name(), query, filters, trigger, action);
+                       reaction = new Reaction(chain.name(), query, filters, merger, action);
                }
 
                reaction.setUpdateInterval(TimeUnit.SECONDS.toMillis(chain.updateInterval()));
diff --git a/src/main/java/net/pterodactylus/rhynodge/mergers/ComicMerger.java b/src/main/java/net/pterodactylus/rhynodge/mergers/ComicMerger.java
new file mode 100644 (file)
index 0000000..5b8f8df
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * rhynodge - ComicMerger.java - Copyright ÂŠ 2013–2021 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.rhynodge.mergers;
+
+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 static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * {@link Merger} implementation that merger two {@link ComicState}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
+ */
+public class ComicMerger implements Merger {
+
+       @Nonnull
+       @Override
+       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");
+
+               ComicState previousComicState = (ComicState) previousState;
+               ComicState currentComicState = (ComicState) currentState;
+
+               List<Comic> allComics = new ArrayList<>(previousComicState.comics());
+               Set<Comic> newComics = new HashSet<>();
+
+               for (Comic comic : currentComicState) {
+                       if (!allComics.contains(comic)) {
+                               allComics.add(comic);
+                               newComics.add(comic);
+                       }
+               }
+
+               return new ComicState(allComics, newComics);
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/rhynodge/mergers/EpisodeMerger.java b/src/main/java/net/pterodactylus/rhynodge/mergers/EpisodeMerger.java
new file mode 100644 (file)
index 0000000..9101c36
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Rhynodge - EpisodeMerger.java - Copyright ÂŠ 2013–2021 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.rhynodge.mergers;
+
+import java.util.ArrayList;
+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 static com.google.common.base.Preconditions.checkState;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * {@link Merger} implementation that merges two {@link EpisodeState}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
+ */
+public class EpisodeMerger implements Merger {
+
+       /**
+        * {@inheritDoc}
+        */
+       @Nonnull
+       @Override
+       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());
+
+               Collection<Episode> newEpisodes = new HashSet<>();
+               Collection<Episode> changedEpisodes = new HashSet<>();
+               Collection<TorrentFile> newTorrentFiles = new HashSet<>();
+               Map<Episode, Episode> allEpisodes = ((EpisodeState) previousState).episodes().stream().collect(toMap(identity(), identity()));
+               for (Episode episode : ((EpisodeState) currentState).episodes()) {
+                       if (!allEpisodes.containsKey(episode)) {
+                               allEpisodes.put(episode, episode);
+                               newEpisodes.add(episode);
+                       }
+                       Episode existingEpisode = allEpisodes.get(episode);
+                       for (TorrentFile torrentFile : new ArrayList<>(episode.torrentFiles())) {
+                               int oldSize = existingEpisode.torrentFiles().size();
+                               existingEpisode.addTorrentFile(torrentFile);
+                               int newSize = existingEpisode.torrentFiles().size();
+                               if (oldSize != newSize) {
+                                       newTorrentFiles.add(torrentFile);
+                               }
+                               if (!newEpisodes.contains(existingEpisode) && (oldSize != newSize)) {
+                                       changedEpisodes.add(existingEpisode);
+                               }
+                       }
+               }
+               return new EpisodeState(allEpisodes.values(), newEpisodes, changedEpisodes, newTorrentFiles);
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/rhynodge/mergers/TorrentMerger.java b/src/main/java/net/pterodactylus/rhynodge/mergers/TorrentMerger.java
new file mode 100644 (file)
index 0000000..f6a75a8
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Rhynodge - TorrentMerger.java - Copyright ÂŠ 2013–2021 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+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 static com.google.common.base.Preconditions.checkState;
+
+/**
+ * {@link Merger} implementation that merges two {@link TorrentState}s, taking
+ * careful note of which {@link TorrentFile}s appear in the current
+ * {@link TorrentState} but not in the previous one.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
+ */
+public class TorrentMerger implements Merger {
+
+       /**
+        * {@inheritDoc}
+        */
+       @Nonnull
+       @Override
+       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());
+
+               Set<TorrentFile> allTorrentFiles = new HashSet<>(((TorrentState) previousState).torrentFiles());
+               Set<TorrentFile> newTorrentFiles = new HashSet<>();
+               for (TorrentFile torrentFile : (TorrentState) currentState) {
+                       if (allTorrentFiles.add(torrentFile)) {
+                               newTorrentFiles.add(torrentFile);
+                       }
+               }
+
+               return new TorrentState(allTorrentFiles, newTorrentFiles);
+       }
+
+}
index 853c9ce..3e9d904 100644 (file)
 
 package net.pterodactylus.rhynodge.output;
 
-import net.pterodactylus.rhynodge.Trigger;
+import net.pterodactylus.rhynodge.State;
 
 /**
- * Defines the output of a {@link Trigger}. As different output has to be
+ * Defines the output of a {@link State}. As different output has to be
  * generated for different media, the {@link #text(String)} method takes as
  * an argument the MIME type of the desired output.
  *
@@ -44,7 +44,7 @@ public interface Output {
         *
         * @param mimeType
         *            The MIME type of the text (“text/plain” and â€œtext/html” should
-        *            be supported by all {@link Trigger}s)
+        *            be supported by all {@link State}s)
         * @return The text for the given MIME type, or {@code null} if there is no
         *         text defined for the given MIME type
         */
index 2e7d9df..a58164d 100644 (file)
@@ -3,7 +3,7 @@
  * <p>
  * A {@link net.pterodactylus.rhynodge.Reaction} consists of three different
  * elements: a {@link net.pterodactylus.rhynodge.Query}, a
- * {@link net.pterodactylus.rhynodge.Trigger}, and an
+ * {@link net.pterodactylus.rhynodge.Merger}, and an
  * {@link net.pterodactylus.rhynodge.Action}.
  * <p>
  * A {@code Query} retrieves the current state of a system; this can simply be
  * <p>
  * After a {@code Query} retrieved the current
  * {@link net.pterodactylus.rhynodge.State} of a system, this state and the
- * previously retrieved state are handed in to a {@code Trigger}. The trigger
- * then decides whether the state of the system can be considered a change.
+ * previously retrieved state are handed in to a {@code Merger}. The merger
+ * takes care of merging the two states in a way that the new state can
+ * {@link net.pterodactylus.rhynodge.State#triggered() decide} whether a
+ * noteworthy change has occured.
  * <p>
  * If a system has been found to trigger, an {@code Action} is executed. It
  * performs arbitrary actions and can use both the current state and the
index e1eff9a..0e661bc 100644 (file)
@@ -68,6 +68,11 @@ public class ComicState extends AbstractState implements Iterable<Comic> {
                return comics.isEmpty();
        }
 
+       @Override
+       public boolean triggered() {
+               return !newComics.isEmpty();
+       }
+
        public List<Comic> comics() {
                return comics;
        }
index e022541..abb387d 100644 (file)
@@ -90,6 +90,11 @@ public class EpisodeState extends AbstractState implements Iterable<Episode> {
                return episodes.isEmpty();
        }
 
+       @Override
+       public boolean triggered() {
+               return !newEpisodes.isEmpty() || !changedEpisodes.isEmpty() || !newTorrentFiles.isEmpty();
+       }
+
        /**
         * Returns all episodes contained in this state.
         *
index f2e9ae2..99cb9d1 100644 (file)
@@ -91,6 +91,11 @@ public class TorrentState extends AbstractState implements Iterable<TorrentFile>
                return files.isEmpty();
        }
 
+       @Override
+       public boolean triggered() {
+               return !newTorrentFiles.isEmpty();
+       }
+
        /**
         * Returns all torrent files of this state.
         *
diff --git a/src/main/java/net/pterodactylus/rhynodge/triggers/AlwaysTrigger.java b/src/main/java/net/pterodactylus/rhynodge/triggers/AlwaysTrigger.java
deleted file mode 100644 (file)
index 0018f50..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Rhynodge - AlwaysTrigger.java - Copyright ÂŠ 2013 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.rhynodge.triggers;
-
-import net.pterodactylus.rhynodge.State;
-import net.pterodactylus.rhynodge.Trigger;
-
-/**
- * {@link Trigger} implementation that always triggers.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
- */
-public class AlwaysTrigger implements Trigger {
-
-       private State currentState;
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * This implementation returns the current state.
-        */
-       @Override
-       public State mergeStates(State previousState, State currentState) {
-               this.currentState = currentState;
-               return currentState;
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * This implementation always returns {@code true}.
-        */
-       @Override
-       public boolean triggers() {
-               return true;
-       }
-
-}
diff --git a/src/main/java/net/pterodactylus/rhynodge/triggers/NewComicTrigger.java b/src/main/java/net/pterodactylus/rhynodge/triggers/NewComicTrigger.java
deleted file mode 100644 (file)
index 16560e2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * rhynodge - NewComicTrigger.java - Copyright ÂŠ 2013 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.rhynodge.triggers;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import net.pterodactylus.rhynodge.State;
-import net.pterodactylus.rhynodge.Trigger;
-import net.pterodactylus.rhynodge.states.ComicState;
-import net.pterodactylus.rhynodge.states.ComicState.Comic;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * {@link Trigger} implementation that detects the presence of new {@link
- * Comic}s in a {@link ComicState}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
- */
-public class NewComicTrigger implements Trigger {
-
-       private boolean triggered = false;
-
-       @Override
-       public State mergeStates(State previousState, State currentState) {
-               checkArgument(previousState instanceof ComicState, "previous state must be a comic state");
-               checkArgument(currentState instanceof ComicState, "current state must be a comic state");
-
-               ComicState previousComicState = (ComicState) previousState;
-               ComicState currentComicState = (ComicState) currentState;
-
-               List<Comic> allComics = new ArrayList<>(previousComicState.comics());
-               Set<Comic> newComics = new HashSet<>();
-
-               for (Comic comic : currentComicState) {
-                       if (!allComics.contains(comic)) {
-                               allComics.add(comic);
-                               newComics.add(comic);
-                               triggered = true;
-                       }
-               }
-
-               return new ComicState(allComics, newComics);
-       }
-
-       @Override
-       public boolean triggers() {
-               return triggered;
-       }
-
-}
diff --git a/src/main/java/net/pterodactylus/rhynodge/triggers/NewEpisodeTrigger.java b/src/main/java/net/pterodactylus/rhynodge/triggers/NewEpisodeTrigger.java
deleted file mode 100644 (file)
index 4b1d209..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Rhynodge - NewEpisodeTrigger.java - Copyright ÂŠ 2013 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.rhynodge.triggers;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-
-import net.pterodactylus.rhynodge.State;
-import net.pterodactylus.rhynodge.Trigger;
-import net.pterodactylus.rhynodge.states.EpisodeState;
-import net.pterodactylus.rhynodge.states.EpisodeState.Episode;
-import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
-
-/**
- * {@link Trigger} implementation that compares two {@link EpisodeState}s for
- * new and changed {@link Episode}s.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
- */
-public class NewEpisodeTrigger implements Trigger {
-
-       private boolean triggered = false;
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public State mergeStates(State previousState, 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());
-
-               Collection<Episode> newEpisodes = new HashSet<>();
-               Collection<Episode> changedEpisodes = new HashSet<>();
-               Collection<TorrentFile> newTorrentFiles = new HashSet<>();
-               Map<Episode, Episode> allEpisodes = ((EpisodeState) previousState).episodes().stream().collect(toMap(identity(), identity()));
-               for (Episode episode : ((EpisodeState) currentState).episodes()) {
-                       if (!allEpisodes.containsKey(episode)) {
-                               allEpisodes.put(episode, episode);
-                               newEpisodes.add(episode);
-                               triggered = true;
-                       }
-                       Episode existingEpisode = allEpisodes.get(episode);
-                       for (TorrentFile torrentFile : new ArrayList<>(episode.torrentFiles())) {
-                               int oldSize = existingEpisode.torrentFiles().size();
-                               existingEpisode.addTorrentFile(torrentFile);
-                               int newSize = existingEpisode.torrentFiles().size();
-                               if (oldSize != newSize) {
-                                       newTorrentFiles.add(torrentFile);
-                               }
-                               if (!newEpisodes.contains(existingEpisode) && (oldSize != newSize)) {
-                                       changedEpisodes.add(existingEpisode);
-                               }
-                       }
-               }
-               return new EpisodeState(allEpisodes.values(), newEpisodes, changedEpisodes, newTorrentFiles);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public boolean triggers() {
-               return triggered;
-       }
-
-}
diff --git a/src/main/java/net/pterodactylus/rhynodge/triggers/NewTorrentTrigger.java b/src/main/java/net/pterodactylus/rhynodge/triggers/NewTorrentTrigger.java
deleted file mode 100644 (file)
index 0ff193c..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Rhynodge - NewTorrentTrigger.java - Copyright ÂŠ 2013 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.rhynodge.triggers;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import net.pterodactylus.rhynodge.State;
-import net.pterodactylus.rhynodge.Trigger;
-import net.pterodactylus.rhynodge.states.TorrentState;
-import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
-
-import static com.google.common.base.Preconditions.checkState;
-
-/**
- * {@link Trigger} implementation that is triggered by {@link TorrentFile}s that
- * appear in the current {@link TorrentState} but not in the previous one.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
- */
-public class NewTorrentTrigger implements Trigger {
-
-       private boolean triggered = false;
-
-       //
-       // TRIGGER METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public State mergeStates(State previousState, 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());
-
-               Set<TorrentFile> allTorrentFiles = new HashSet<>(((TorrentState) previousState).torrentFiles());
-               Set<TorrentFile> newTorrentFiles = new HashSet<>();
-               for (TorrentFile torrentFile : (TorrentState) currentState) {
-                       if (allTorrentFiles.add(torrentFile)) {
-                               newTorrentFiles.add(torrentFile);
-                               triggered = true;
-                       }
-               }
-
-               return new TorrentState(allTorrentFiles, newTorrentFiles);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public boolean triggers() {
-               return triggered;
-       }
-
-}
index 1355408..0fd1105 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.AbstruseGooseComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Abstruse Goose comics.
@@ -33,7 +33,7 @@ import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 public class AbstruseGooseWatcher extends DefaultWatcher {
 
        public AbstruseGooseWatcher() {
-               super(new HttpQuery("http://abstrusegoose.com/"), Arrays.asList(new HtmlFilter(), new AbstruseGooseComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://abstrusegoose.com/"), Arrays.asList(new HtmlFilter(), new AbstruseGooseComicFilter()), new ComicMerger());
        }
 
 }
index fedcc7f..804160b 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.Collar6ComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Collar 6 comics.
@@ -34,7 +34,7 @@ public class Collar6Watcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public Collar6Watcher() {
-               super(new HttpQuery("http://collar6.com/"), Arrays.asList(new HtmlFilter(), new Collar6ComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://collar6.com/"), Arrays.asList(new HtmlFilter(), new Collar6ComicFilter()), new ComicMerger());
        }
 
 }
index ff5db70..ad93e1f 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.CtrlAltDelComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Ctrl Alt Del comics.
@@ -34,7 +34,7 @@ public class CtrlAltDelWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public CtrlAltDelWatcher() {
-               super(new HttpQuery("http://www.cad-comic.com/cad/"), Arrays.asList(new HtmlFilter(), new CtrlAltDelComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.cad-comic.com/cad/"), Arrays.asList(new HtmlFilter(), new CtrlAltDelComicFilter()), new ComicMerger());
        }
 
 }
index 11971f6..331ec4b 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.CyanideAndHappinessComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Cyanide and Happiness
@@ -35,7 +35,7 @@ public class CyanideAndHappinessWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public CyanideAndHappinessWatcher() {
-               super(new HttpQuery("http://www.explosm.net/comics/new/"), Arrays.asList(new HtmlFilter(), new CyanideAndHappinessComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.explosm.net/comics/new/"), Arrays.asList(new HtmlFilter(), new CyanideAndHappinessComicFilter()), new ComicMerger());
        }
 
 }
index fb6b218..9dc2930 100644 (file)
@@ -22,8 +22,8 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
+import net.pterodactylus.rhynodge.Merger;
 
 /**
  * Abstract base implementation of a {@link Watcher}.
@@ -38,8 +38,8 @@ public class DefaultWatcher implements Watcher {
        /** The filters of the watcher. */
        private final List<Filter> filters = new ArrayList<Filter>();
 
-       /** The trigger of the watcher. */
-       private final Trigger trigger;
+       /** The merger of the watcher. */
+       private final Merger merger;
 
        /**
         * Creates a new default watcher.
@@ -48,13 +48,13 @@ public class DefaultWatcher implements Watcher {
         *            The query of the watcher
         * @param filters
         *            The filters of the watcher
-        * @param trigger
-        *            The trigger of the watcher
+        * @param merger
+        *            The merger of the watcher
         */
-       protected DefaultWatcher(Query query, List<Filter> filters, Trigger trigger) {
+       protected DefaultWatcher(Query query, List<Filter> filters, Merger merger) {
                this.query = query;
                this.filters.addAll(filters);
-               this.trigger = trigger;
+               this.merger = merger;
        }
 
        //
@@ -77,12 +77,8 @@ public class DefaultWatcher implements Watcher {
                return filters;
        }
 
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public Trigger trigger() {
-               return trigger;
+       public Merger merger() {
+               return merger;
        }
 
 }
index 98f5c5c..08d7d8f 100644 (file)
@@ -2,11 +2,11 @@ package net.pterodactylus.rhynodge.watchers
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter
 import net.pterodactylus.rhynodge.filters.comics.DrugsAndWiresComicFilter
+import net.pterodactylus.rhynodge.mergers.ComicMerger
 import net.pterodactylus.rhynodge.queries.HttpQuery
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger
 
-class DrugsAndWiresWatcher: DefaultWatcher(query, filters, trigger)
+class DrugsAndWiresWatcher: DefaultWatcher(query, filters, merger)
 
 private val query = HttpQuery("https://www.drugsandwires.fail/")
 private val filters = listOf(HtmlFilter(), DrugsAndWiresComicFilter())
-private val trigger = NewComicTrigger()
+private val merger = ComicMerger()
index 1bb999c..f57762c 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.GeneralProtectionFaultComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new General Protection Fault
@@ -35,7 +35,7 @@ public class GeneralProtectionFaultWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public GeneralProtectionFaultWatcher() {
-               super(new HttpQuery("http://www.gpf-comics.com/"), Arrays.asList(new HtmlFilter(), new GeneralProtectionFaultComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.gpf-comics.com/"), Arrays.asList(new HtmlFilter(), new GeneralProtectionFaultComicFilter()), new ComicMerger());
        }
 
 }
index dd9703f..e1aa4a2 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.GirlGeniusComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Genius Girl comics.
@@ -34,7 +34,7 @@ public class GirlGeniusWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public GirlGeniusWatcher() {
-               super(new HttpQuery("http://www.girlgeniusonline.com/comic.php"), Arrays.asList(new HtmlFilter(), new GirlGeniusComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.girlgeniusonline.com/comic.php"), Arrays.asList(new HtmlFilter(), new GirlGeniusComicFilter()), new ComicMerger());
        }
 
 }
index 19a8513..593f8ff 100644 (file)
@@ -5,8 +5,8 @@ import static java.util.Arrays.asList;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.HeldentageFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new â€œHeldentage” comics.
@@ -16,7 +16,7 @@ import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 public class HeldentageWatcher extends DefaultWatcher {
 
        public HeldentageWatcher() {
-               super(new HttpQuery("http://www.der-flix.de/"), asList(new HtmlFilter(), new HeldentageFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.der-flix.de/"), asList(new HtmlFilter(), new HeldentageFilter()), new ComicMerger());
        }
 
 }
index f96f940..6826e25 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.KevinAndKellComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Kevin and Kell comics.
@@ -34,7 +34,7 @@ public class KevinAndKellWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public KevinAndKellWatcher() {
-               super(new HttpQuery("http://www.kevinandkell.com/"), Arrays.asList(new HtmlFilter(), new KevinAndKellComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.kevinandkell.com/"), Arrays.asList(new HtmlFilter(), new KevinAndKellComicFilter()), new ComicMerger());
        }
 
 }
index ee8c1f3..0fec686 100644 (file)
@@ -25,14 +25,13 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.EpisodeFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.KickAssTorrentsFilter;
+import net.pterodactylus.rhynodge.mergers.EpisodeMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewEpisodeTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -51,7 +50,7 @@ public class KickAssTorrentsEpisodeWatcher extends DefaultWatcher {
         *            The terms to search for
         */
        public KickAssTorrentsEpisodeWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new EpisodeMerger());
        }
 
        //
@@ -83,13 +82,4 @@ public class KickAssTorrentsEpisodeWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new KickAssTorrentsFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter(), new EpisodeFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewEpisodeTrigger();
-       }
-
 }
index 6fa449d..231c086 100644 (file)
@@ -25,13 +25,12 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.KickAssTorrentsFilter;
+import net.pterodactylus.rhynodge.mergers.TorrentMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewTorrentTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -49,7 +48,7 @@ public class KickAssTorrentsWatcher extends DefaultWatcher {
         *            The terms to search for
         */
        public KickAssTorrentsWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new TorrentMerger());
        }
 
        //
@@ -81,13 +80,4 @@ public class KickAssTorrentsWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new KickAssTorrentsFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewTorrentTrigger();
-       }
-
 }
index d88f574..13d48c9 100644 (file)
@@ -25,8 +25,8 @@ import net.pterodactylus.rhynodge.filters.ExtractUrlFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.HttpQueryFilter;
 import net.pterodactylus.rhynodge.filters.comics.LeastICouldDoComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
@@ -43,7 +43,7 @@ public class LeastICouldDoWatcher extends DefaultWatcher {
 
        /** Creates a new â€œLeast I Could Do” watcher. */
        public LeastICouldDoWatcher() {
-               super(new HttpQuery("http://www.leasticoulddo.com/"), createFilters(), new NewComicTrigger());
+               super(new HttpQuery("http://www.leasticoulddo.com/"), createFilters(), new ComicMerger());
        }
 
        //
index 4fec730..eed6c90 100644 (file)
@@ -25,15 +25,14 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.EpisodeFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.PirateBayFilter;
+import net.pterodactylus.rhynodge.mergers.EpisodeMerger;
 import net.pterodactylus.rhynodge.queries.FallbackQuery;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewEpisodeTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -51,7 +50,7 @@ public class PirateBayEpisodeWatcher extends DefaultWatcher {
         *            The terms to search for
         */
        public PirateBayEpisodeWatcher(String searchTerms, String proxy) {
-               super(createHttpQuery(searchTerms, extractProxyHost(proxy), extractProxyPort(proxy)), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms, extractProxyHost(proxy), extractProxyPort(proxy)), createFilters(), new EpisodeMerger());
        }
 
        private static String extractProxyHost(String proxy) {
@@ -87,13 +86,4 @@ public class PirateBayEpisodeWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new PirateBayFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter(), new EpisodeFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewEpisodeTrigger();
-       }
-
 }
index 1a62e80..2254fba 100644 (file)
@@ -25,14 +25,13 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.PirateBayFilter;
+import net.pterodactylus.rhynodge.mergers.TorrentMerger;
 import net.pterodactylus.rhynodge.queries.FallbackQuery;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewTorrentTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -50,7 +49,7 @@ public class PirateBayWatcher extends DefaultWatcher {
         *            The terms to search for
         */
        public PirateBayWatcher(String searchTerms, String proxy) {
-               super(createHttpQuery(searchTerms, extractProxyHost(proxy), extractProxyPort(proxy)), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms, extractProxyHost(proxy), extractProxyPort(proxy)), createFilters(), new TorrentMerger());
        }
 
        private static String extractProxyHost(String proxy) {
@@ -93,13 +92,4 @@ public class PirateBayWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new PirateBayFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewTorrentTrigger();
-       }
-
 }
index dc1f33c..0ea2849 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.PoorlyDrawnLinesComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Poorly Drawn Lines
@@ -35,7 +35,7 @@ public class PoorlyDrawnLinesWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public PoorlyDrawnLinesWatcher() {
-               super(new HttpQuery("http://poorlydrawnlines.com/"), Arrays.asList(new HtmlFilter(), new PoorlyDrawnLinesComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://poorlydrawnlines.com/"), Arrays.asList(new HtmlFilter(), new PoorlyDrawnLinesComicFilter()), new ComicMerger());
        }
 
 }
index 769d1dd..3e90a00 100644 (file)
@@ -22,8 +22,8 @@ import java.util.Arrays;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.SaturdayMorningBreakfastCerealComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link Watcher} implementation that watches for new Saturday Morning
@@ -35,7 +35,7 @@ public class SaturdayMorningBreakfastCerealWatcher extends DefaultWatcher {
 
        /** Creates a new watcher for Cyanide and Happiness comics. */
        public SaturdayMorningBreakfastCerealWatcher() {
-               super(new HttpQuery("http://www.smbc-comics.com/"), Arrays.asList(new HtmlFilter(), new SaturdayMorningBreakfastCerealComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.smbc-comics.com/"), Arrays.asList(new HtmlFilter(), new SaturdayMorningBreakfastCerealComicFilter()), new ComicMerger());
        }
 
 }
index 0a67f6f..e797d18 100644 (file)
@@ -5,8 +5,8 @@ import static java.util.Arrays.asList;
 import net.pterodactylus.rhynodge.Watcher;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.webpages.savoy.SavoyTicketsFilter;
+import net.pterodactylus.rhynodge.mergers.LastStateMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.AlwaysTrigger;
 
 /**
  * {@link Watcher} implementation that shows tickets sold in the Savoy theatre.
@@ -22,7 +22,7 @@ public class SavoyTicketWatcher extends DefaultWatcher {
                                                new HtmlFilter(),
                                                new SavoyTicketsFilter()
                                ),
-                               new AlwaysTrigger()
+                               new LastStateMerger()
                );
        }
 
index fdcae7d..cb7775f 100644 (file)
@@ -24,8 +24,8 @@ import net.pterodactylus.rhynodge.filters.ExtractUrlFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.HttpQueryFilter;
 import net.pterodactylus.rhynodge.filters.comics.ScandinaviaAndTheWorldComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
@@ -40,7 +40,7 @@ import org.jsoup.select.Elements;
 public class ScandinaviaAndTheWorldWatcher extends DefaultWatcher {
 
        public ScandinaviaAndTheWorldWatcher() {
-               super(new HttpQuery("http://satwcomic.com/"), createFilters(), new NewComicTrigger());
+               super(new HttpQuery("http://satwcomic.com/"), createFilters(), new ComicMerger());
        }
 
        private static List<Filter> createFilters() {
index b273004..914f614 100644 (file)
@@ -21,8 +21,8 @@ import java.util.Arrays;
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.SinfestComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link net.pterodactylus.rhynodge.Watcher} implementation that watches
@@ -33,7 +33,7 @@ import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 public class SinfestWatcher extends DefaultWatcher {
 
        public SinfestWatcher() {
-               super(new HttpQuery("http://www.sinfest.net/"), Arrays.asList(new HtmlFilter(), new SinfestComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://www.sinfest.net/"), Arrays.asList(new HtmlFilter(), new SinfestComicFilter()), new ComicMerger());
        }
 
 }
index bc22f92..1a47ffa 100644 (file)
@@ -25,13 +25,12 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.filters.EpisodeFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.TorrentHoundFilter;
+import net.pterodactylus.rhynodge.mergers.EpisodeMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewEpisodeTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -50,7 +49,7 @@ public class TorrentHoundEpisodeWatcher extends DefaultWatcher {
         *              The terms to search for
         */
        public TorrentHoundEpisodeWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new EpisodeMerger());
        }
 
        //
@@ -82,13 +81,4 @@ public class TorrentHoundEpisodeWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new TorrentHoundFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter(), new EpisodeFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewEpisodeTrigger();
-       }
-
 }
index 4a9fba5..4df53a6 100644 (file)
@@ -25,12 +25,11 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.TorrentHoundFilter;
+import net.pterodactylus.rhynodge.mergers.TorrentMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewTorrentTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -49,7 +48,7 @@ public class TorrentHoundWatcher extends DefaultWatcher {
         *              The terms to search for
         */
        public TorrentHoundWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new TorrentMerger());
        }
 
        //
@@ -81,13 +80,4 @@ public class TorrentHoundWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new TorrentHoundFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewTorrentTrigger();
-       }
-
 }
index 920d3bc..a212359 100644 (file)
@@ -25,13 +25,12 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
 import net.pterodactylus.rhynodge.filters.EpisodeFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.TorrentzEuFilter;
+import net.pterodactylus.rhynodge.mergers.EpisodeMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewEpisodeTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -50,7 +49,7 @@ public class TorrentzEuEpisodeWatcher extends DefaultWatcher {
         *              The terms to search for
         */
        public TorrentzEuEpisodeWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new EpisodeMerger());
        }
 
        //
@@ -82,13 +81,4 @@ public class TorrentzEuEpisodeWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new TorrentzEuFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter(), new EpisodeFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewEpisodeTrigger();
-       }
-
 }
index a5192a3..a709d0d 100644 (file)
@@ -25,13 +25,11 @@ import java.util.List;
 
 import net.pterodactylus.rhynodge.Filter;
 import net.pterodactylus.rhynodge.Query;
-import net.pterodactylus.rhynodge.Trigger;
-import net.pterodactylus.rhynodge.filters.EpisodeFilter;
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.SizeBlacklistFilter;
 import net.pterodactylus.rhynodge.filters.torrents.TorrentzEuFilter;
+import net.pterodactylus.rhynodge.mergers.TorrentMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewEpisodeTrigger;
 
 import com.google.common.collect.ImmutableList;
 
@@ -50,7 +48,7 @@ public class TorrentzEuWatcher extends DefaultWatcher {
         *              The terms to search for
         */
        public TorrentzEuWatcher(String searchTerms) {
-               super(createHttpQuery(searchTerms), createFilters(), createTrigger());
+               super(createHttpQuery(searchTerms), createFilters(), new TorrentMerger());
        }
 
        //
@@ -82,13 +80,4 @@ public class TorrentzEuWatcher extends DefaultWatcher {
                return ImmutableList.of(new HtmlFilter(), new TorrentzEuFilter(), createDefaultBlacklistFilter(), new SizeBlacklistFilter());
        }
 
-       /**
-        * Creates the trigger of the watcher.
-        *
-        * @return The trigger of the watcher
-        */
-       private static Trigger createTrigger() {
-               return new NewEpisodeTrigger();
-       }
-
 }
index 360652a..9829d82 100644 (file)
@@ -21,8 +21,8 @@ import java.util.Arrays;
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter;
 import net.pterodactylus.rhynodge.filters.comics.XkcdComicFilter;
+import net.pterodactylus.rhynodge.mergers.ComicMerger;
 import net.pterodactylus.rhynodge.queries.HttpQuery;
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger;
 
 /**
  * {@link net.pterodactylus.rhynodge.Watcher} implementation that watches XKCD
@@ -34,7 +34,7 @@ public class XkcdWatcher extends DefaultWatcher {
 
        /** Creates a new XKCD watcher. */
        public XkcdWatcher() {
-               super(new HttpQuery("http://xkcd.com/"), Arrays.asList(new HtmlFilter(), new XkcdComicFilter()), new NewComicTrigger());
+               super(new HttpQuery("http://xkcd.com/"), Arrays.asList(new HtmlFilter(), new XkcdComicFilter()), new ComicMerger());
        }
 
 }
diff --git a/src/main/kotlin/net/pterodactylus/rhynodge/Merger.kt b/src/main/kotlin/net/pterodactylus/rhynodge/Merger.kt
new file mode 100644 (file)
index 0000000..a9807bf
--- /dev/null
@@ -0,0 +1,7 @@
+package net.pterodactylus.rhynodge
+
+interface Merger {
+
+       fun mergeStates(previousState: State, currentState: State): State
+
+}
diff --git a/src/main/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMerger.kt b/src/main/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMerger.kt
new file mode 100644 (file)
index 0000000..fe1fb37
--- /dev/null
@@ -0,0 +1,13 @@
+package net.pterodactylus.rhynodge.mergers
+
+import net.pterodactylus.rhynodge.Merger
+import net.pterodactylus.rhynodge.State
+
+/**
+ * [Merger] implementation that always returns the current state.
+ */
+class LastStateMerger : Merger {
+
+       override fun mergeStates(previousState: State, currentState: State) = currentState
+
+}
index 94fe96d..e6d288a 100644 (file)
@@ -2,11 +2,11 @@ package net.pterodactylus.rhynodge.watchers
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter
 import net.pterodactylus.rhynodge.filters.comics.BusinessCatComicFilter
+import net.pterodactylus.rhynodge.mergers.ComicMerger
 import net.pterodactylus.rhynodge.queries.HttpQuery
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger
 
-class BusinessCatWatcher : DefaultWatcher(query, filters, trigger)
+class BusinessCatWatcher : DefaultWatcher(query, filters, merger)
 
 private val query = HttpQuery("https://www.businesscatcomic.com/")
 private val filters = listOf(HtmlFilter(), BusinessCatComicFilter())
-private val trigger = NewComicTrigger()
+private val merger = ComicMerger()
index 417e2c2..dfa5cb0 100644 (file)
@@ -18,11 +18,11 @@ package net.pterodactylus.rhynodge.watchers
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter
 import net.pterodactylus.rhynodge.filters.comics.QuestionableContentComicFilter
+import net.pterodactylus.rhynodge.mergers.ComicMerger
 import net.pterodactylus.rhynodge.queries.HttpQuery
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger
 
-class QuestionableContentWatcher : DefaultWatcher(query, filters, trigger)
+class QuestionableContentWatcher : DefaultWatcher(query, filters, merger)
 
 private val query = HttpQuery("https://www.questionablecontent.net/")
 private val filters = listOf(HtmlFilter(), QuestionableContentComicFilter())
-private val trigger = NewComicTrigger()
+private val merger = ComicMerger()
index e68e218..80edc4b 100644 (file)
@@ -2,11 +2,11 @@ package net.pterodactylus.rhynodge.watchers
 
 import net.pterodactylus.rhynodge.filters.HtmlFilter
 import net.pterodactylus.rhynodge.filters.comics.SoggyCardboardComicFilter
+import net.pterodactylus.rhynodge.mergers.ComicMerger
 import net.pterodactylus.rhynodge.queries.HttpQuery
-import net.pterodactylus.rhynodge.triggers.NewComicTrigger
 
-class SoggyCardboardWatcher : DefaultWatcher(query, filters, trigger)
+class SoggyCardboardWatcher : DefaultWatcher(query, filters, merger)
 
 private val query = HttpQuery("http://www.soggycardboard.com/")
 private val filters = listOf(HtmlFilter(), SoggyCardboardComicFilter())
-private val trigger = NewComicTrigger()
+private val merger = ComicMerger()
index 214bfb9..a596d12 100644 (file)
@@ -2,11 +2,11 @@ package net.pterodactylus.rhynodge.watchers
 
 import net.pterodactylus.rhynodge.filters.*
 import net.pterodactylus.rhynodge.filters.comics.*
+import net.pterodactylus.rhynodge.mergers.ComicMerger
 import net.pterodactylus.rhynodge.queries.*
-import net.pterodactylus.rhynodge.triggers.*
 
-class TheMonsterUnderTheBedWatcher : DefaultWatcher(query, filters, trigger)
+class TheMonsterUnderTheBedWatcher : DefaultWatcher(query, filters, merger)
 
 private val query = HttpQuery("http://themonsterunderthebed.net/")
 private val filters = listOf(HtmlFilter(), TheMonsterUnderTheBedFilter())
-private val trigger = NewComicTrigger()
+private val merger = ComicMerger()
index 32bc1a2..febdfa5 100644 (file)
@@ -25,9 +25,9 @@
                }
        ],
 
-       "trigger":
+       "merger":
                {
-                       "class": "NewEpisodeTrigger"
+                       "class": "EpisodeMerger"
                },
 
        "action":
index 09e07e1..78a892f 100644 (file)
@@ -22,9 +22,9 @@
                }
        ],
 
-       "trigger":
+       "merger":
                {
-                       "class": "NewTorrentTrigger"
+                       "class": "TorrentMerger"
                },
 
        "action":
index 4ddf3ae..c30013f 100644 (file)
@@ -18,10 +18,10 @@ public class ReactionTest {
 
        private final Query query = mock(Query.class);
        private final Filter filter = mock(Filter.class);
-       private final Trigger trigger = mock(Trigger.class);
+       private final Merger merger = mock(Merger.class);
        private final Action action = mock(Action.class);
-       private final Reaction reactionWithoutFilters = new Reaction("without", query, trigger, action);
-       private final Reaction reactionWithFilters = new Reaction("with", query, asList(filter), trigger, action);
+       private final Reaction reactionWithoutFilters = new Reaction("without", query, merger, action);
+       private final Reaction reactionWithFilters = new Reaction("with", query, asList(filter), merger, action);
 
        @Test
        public void reactionStoresNameCorrectly() {
@@ -42,9 +42,9 @@ public class ReactionTest {
        }
 
        @Test
-       public void reactionStoresTriggerCorrectly() {
-               assertThat(reactionWithoutFilters.trigger(), is(trigger));
-               assertThat(reactionWithFilters.trigger(), is(trigger));
+       public void reactionStoresMergerCorrectly() {
+               assertThat(reactionWithoutFilters.merger(), is(merger));
+               assertThat(reactionWithFilters.merger(), is(merger));
        }
 
        @Test
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/mergers/ComicMergerTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/mergers/ComicMergerTest.kt
new file mode 100644 (file)
index 0000000..3f07181
--- /dev/null
@@ -0,0 +1,23 @@
+package net.pterodactylus.rhynodge.mergers
+
+import net.pterodactylus.rhynodge.states.ComicState
+import net.pterodactylus.rhynodge.states.ComicState.Comic
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.contains
+import org.junit.Test
+
+class ComicMergerTest {
+
+       private val comicMerger = ComicMerger()
+
+       @Test
+       fun `comic merger does not reorder comics`() {
+               val oldComicState = ComicState(generateListOfComics())
+               val newComicState = ComicState(generateListOfComics().plusElement(Comic("new 1")))
+               val mergedComicState = comicMerger.mergeStates(oldComicState, newComicState) as ComicState
+               assertThat(mergedComicState.comics(), contains(*generateListOfComics().plusElement(Comic("new 1")).toTypedArray()))
+       }
+
+}
+
+private fun generateListOfComics(): List<Comic> = (1..40).map { Comic("comic $it") }
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMergerTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/mergers/LastStateMergerTest.kt
new file mode 100644 (file)
index 0000000..6046bb3
--- /dev/null
@@ -0,0 +1,20 @@
+package net.pterodactylus.rhynodge.mergers
+
+import net.pterodactylus.rhynodge.State
+import net.pterodactylus.rhynodge.states.StateManagerTest.TestState
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.sameInstance
+import org.junit.Test
+
+class LastStateMergerTest {
+
+       @Test
+       fun `merging states returns the current state`() {
+               assertThat(merger.mergeStates(previousState, successfulState), sameInstance(successfulState))
+       }
+
+       private val merger = LastStateMerger()
+       private val previousState = TestState()
+       private val successfulState: State = TestState()
+
+}
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/triggers/AlwaysTriggerTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/triggers/AlwaysTriggerTest.kt
deleted file mode 100644 (file)
index 6945a0e..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.pterodactylus.rhynodge.triggers
-
-import net.pterodactylus.rhynodge.State
-import net.pterodactylus.rhynodge.states.FailedState
-import net.pterodactylus.rhynodge.states.StateManagerTest.TestState
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.sameInstance
-import org.junit.Test
-
-class AlwaysTriggerTest {
-
-       @Test
-       fun `merging states returns the current state`() {
-               assertThat(trigger.mergeStates(previousState, successfulState), sameInstance(successfulState))
-       }
-
-       @Test
-       fun `successful state triggers`() {
-               trigger.mergeStates(previousState, successfulState)
-               assertThat(trigger.triggers(), equalTo(true))
-       }
-
-       @Test
-       fun `failed state also triggers`() {
-               trigger.mergeStates(previousState, failedState)
-               assertThat(trigger.triggers(), equalTo(true))
-       }
-
-       private val trigger = AlwaysTrigger()
-       private val previousState = TestState()
-       private val successfulState: State = TestState()
-       private val failedState: State = FailedState()
-
-}
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/triggers/NewComicTriggerTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/triggers/NewComicTriggerTest.kt
deleted file mode 100644 (file)
index 967a890..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package net.pterodactylus.rhynodge.triggers
-
-import net.pterodactylus.rhynodge.states.ComicState
-import net.pterodactylus.rhynodge.states.ComicState.Comic
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-
-class NewComicTriggerTest {
-
-       private val newComicTrigger = NewComicTrigger()
-
-       @Test
-       fun `comic trigger recognizes there are no new comic`() {
-               val oldComicState = ComicState(generateListOfComics())
-               val newComicState = ComicState(generateListOfComics())
-               newComicTrigger.mergeStates(oldComicState, newComicState)
-               assertThat(newComicTrigger.triggers(), equalTo(false))
-       }
-
-       @Test
-       fun `comic trigger recognizes new comics`() {
-               val oldComicState = ComicState(generateListOfComics())
-               val newComicState = ComicState(generateListOfComics().plusElement(Comic("new 1")))
-               newComicTrigger.mergeStates(oldComicState, newComicState)
-               assertThat(newComicTrigger.triggers(), equalTo(true))
-       }
-
-       @Test
-       fun `comic trigger does not reorder comics`() {
-               val oldComicState = ComicState(generateListOfComics())
-               val newComicState = ComicState(generateListOfComics().plusElement(Comic("new 1")))
-               val mergedComicState = newComicTrigger.mergeStates(oldComicState, newComicState) as ComicState
-               assertThat(mergedComicState.comics(), contains(*generateListOfComics().plusElement(Comic("new 1")).toTypedArray()))
-       }
-
-}
-
-private fun generateListOfComics(): List<Comic> = (1..40).map { Comic("comic $it") }