✨ Allow triggering states from anywhere
[rhynodge.git] / src / main / java / net / pterodactylus / rhynodge / states / AbstractState.java
index afb7dc2..ecd520f 100644 (file)
 
 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;
+import net.pterodactylus.rhynodge.output.DefaultOutput;
+import net.pterodactylus.rhynodge.output.Output;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.google.common.escape.Escaper;
+import com.google.common.html.HtmlEscapers;
 
 /**
  * Abstract implementation of a {@link State} that knows about the basic
@@ -36,8 +46,10 @@ public abstract class AbstractState implements State {
        private final long time;
 
        /** Whether the state was successfully retrieved. */
+       @JsonProperty
        private final boolean success;
        private final boolean empty;
+       private boolean triggered = false;
 
        /** The optional exception that occured while retrieving the state. */
        private final Throwable exception;
@@ -61,11 +73,26 @@ public abstract class AbstractState implements State {
         *            otherwise
         */
        protected AbstractState(boolean success) {
-               this(success, true, null);
+               this(Clock.systemUTC(), success);
+       }
+
+       /**
+        * Creates a new state.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param success {@code true} if the state is successful, {@code false}
+        *              otherwise
+        */
+       protected AbstractState(Clock clock, boolean success) {
+               this(clock, success, true, null);
        }
 
        protected AbstractState(boolean success, boolean empty) {
-               this(success, empty, null);
+               this(Clock.systemUTC(), success, empty);
+       }
+
+       protected AbstractState(Clock clock, boolean success, boolean empty) {
+               this(clock, success, empty, null);
        }
 
        /**
@@ -75,7 +102,17 @@ public abstract class AbstractState implements State {
         *            The exception that occured while retrieving the state
         */
        protected AbstractState(Throwable exception) {
-               this(false, true, exception);
+               this(Clock.systemUTC(), exception);
+       }
+
+       /**
+        * Creates a new non-successful state with the given exception.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param exception The exception that occured while retrieving the state
+        */
+       protected AbstractState(Clock clock, Throwable exception) {
+               this(clock, false, true, exception);
        }
 
        /**
@@ -88,7 +125,19 @@ public abstract class AbstractState implements State {
         *            The exception that occured while retrieving the state
         */
        protected AbstractState(boolean success, boolean empty, Throwable exception) {
-               this.time = System.currentTimeMillis();
+               this(Clock.systemUTC(), success, empty, exception);
+       }
+
+       /**
+        * Creates a new state.
+        *
+        * @param clock The clock for generating {@link #time}
+        * @param success {@code true} if the state is successful, {@code false}
+        *              otherwise
+        * @param exception The exception that occured while retrieving the state
+        */
+       protected AbstractState(Clock clock, boolean success, boolean empty, Throwable exception) {
+               this.time = clock.millis();
                this.success = success;
                this.empty = empty;
                this.exception = exception;
@@ -119,6 +168,16 @@ public abstract class AbstractState implements State {
                return empty;
        }
 
+       @Override
+       public boolean triggered() {
+               return triggered;
+       }
+
+       @Override
+       public void trigger() {
+               triggered = true;
+       }
+
        /**
         * {@inheritDoc}
         */
@@ -143,4 +202,43 @@ public abstract class AbstractState implements State {
                return exception;
        }
 
+       @Nonnull
+       @Override
+       public Output output(Reaction reaction) {
+               return new DefaultOutput(summary(reaction))
+                               .addText("text/plain", plainText())
+                               .addText("text/html", htmlText());
+       }
+
+       @Nonnull
+       protected String summary(Reaction reaction) {
+               return reaction.name();
+       }
+
+       @Nonnull
+       protected abstract String plainText();
+
+       @Nullable
+       protected String htmlText() {
+               //noinspection UnstableApiUsage
+               return "<div>" + htmlEscaper.escape(plainText()) + "</div>";
+       }
+
+       @SuppressWarnings("UnstableApiUsage")
+       private static final Escaper htmlEscaper = HtmlEscapers.htmlEscaper();
+
+       @Override
+       public int hashCode() {
+               return Objects.hash(success, empty, time, failCount, exception);
+       }
+
+       @Override
+       public boolean equals(Object object) {
+               if (!(object instanceof AbstractState)) {
+                       return false;
+               }
+               AbstractState abstractState = (AbstractState) object;
+               return (success == abstractState.success) && (empty == abstractState.empty) && (time == abstractState.time) && (failCount == abstractState.failCount) && Objects.equals(exception, abstractState.exception);
+       }
+
 }