From d34c316407ec88349ef198497b61bbc7a9b495ff Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Mon, 12 Nov 2018 15:38:37 +0100
Subject: [PATCH] Add StateChangeEvent for state changes of entities

---
 .../thm/arsnova/event/StateChangeEvent.java   | 45 +++++++++++
 .../arsnova/event/StateEventDispatcher.java   | 75 +++++++++++++++++++
 2 files changed, 120 insertions(+)
 create mode 100644 src/main/java/de/thm/arsnova/event/StateChangeEvent.java
 create mode 100644 src/main/java/de/thm/arsnova/event/StateEventDispatcher.java

diff --git a/src/main/java/de/thm/arsnova/event/StateChangeEvent.java b/src/main/java/de/thm/arsnova/event/StateChangeEvent.java
new file mode 100644
index 000000000..69b557981
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/StateChangeEvent.java
@@ -0,0 +1,45 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Entity;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.core.ResolvableType;
+import org.springframework.core.ResolvableTypeProvider;
+
+import java.util.Optional;
+
+public class StateChangeEvent<E extends Entity, T> extends ApplicationEvent implements ResolvableTypeProvider {
+	private final E entity;
+	private final String stateName;
+	private final T newValue;
+	private final T oldValue;
+
+	public StateChangeEvent(final Object source, final E entity, final String stateName,
+			final T newValue, final T oldValue) {
+		super(source);
+		this.entity = entity;
+		this.stateName = stateName;
+		this.newValue = newValue;
+		this.oldValue = oldValue;
+	}
+
+	public E getEntity() {
+		return entity;
+	}
+
+	public String getStateName() {
+		return stateName;
+	}
+
+	public T getNewValue() {
+		return newValue;
+	}
+
+	public Optional<T> getOldValue() {
+		return Optional.ofNullable(oldValue);
+	}
+
+	@Override
+	public ResolvableType getResolvableType() {
+		return ResolvableType.forClassWithGenerics(getClass(), entity.getClass(), newValue.getClass());
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java b/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java
new file mode 100644
index 000000000..7d4993ad6
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/event/StateEventDispatcher.java
@@ -0,0 +1,75 @@
+package de.thm.arsnova.event;
+
+import de.thm.arsnova.model.Content;
+import de.thm.arsnova.model.Entity;
+import de.thm.arsnova.model.Room;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * StateEventDispatcher publishes additional, more specific events for state changes of entities when
+ * {@link AfterUpdateEvent}s are received.
+ *
+ * @author Daniel Gerhardt
+ */
+@Component
+public class StateEventDispatcher implements ApplicationEventPublisherAware {
+	private ApplicationEventPublisher eventPublisher;
+
+	@EventListener
+	public void dispatchRoomStateEvent(final AfterFullUpdateEvent<Room> event) {
+		final Room newRoom = event.getEntity();
+		final Room oldRoom = event.getOldEntity();
+		publishEventIfPropertyChanged(newRoom, oldRoom, Room::isClosed, "closed");
+		publishEventIfPropertyChanged(newRoom, oldRoom, Room::getSettings, "settings");
+	}
+
+	@EventListener
+	public void dispatchRoomStateEvent(final AfterPatchEvent<Room> event) {
+		final Room room = event.getEntity();
+		final Map<String, Object> changes = event.getChanges();
+		publishEventIfPropertyChanged(room, changes, "closed", "closed");
+		publishEventIfPropertyChanged(room, changes, "settings", "settings");
+	}
+
+	@EventListener
+	public void dispatchContentStateEvent(final AfterFullUpdateEvent<Content> event) {
+		final Content newContent = event.getEntity();
+		final Content oldContent = event.getOldEntity();
+		publishEventIfPropertyChanged(newContent, oldContent, Content::getState, "state");
+	}
+
+	@EventListener
+	public void dispatchContentStateEvent(final AfterPatchEvent<Content> event) {
+		final Content content = event.getEntity();
+		final Map<String, Object> changes = event.getChanges();
+		publishEventIfPropertyChanged(content, changes, "state", "state");
+	}
+
+	private <E extends Entity, T extends Object> void publishEventIfPropertyChanged(
+			final E newEntity, final E oldEntity, final Function<E, T> propertyGetter, final String stateName) {
+		T newValue = propertyGetter.apply(newEntity);
+		T oldValue = propertyGetter.apply(oldEntity);
+		if (!newValue.equals(oldValue)) {
+			eventPublisher.publishEvent(new StateChangeEvent<>(this, newEntity, stateName, newValue, oldValue));
+		}
+	}
+
+	private <E extends Entity> void publishEventIfPropertyChanged(
+			final E entity, final Map<String, Object> changes, final String property, final String stateName) {
+		if (changes.containsKey(property)) {
+			eventPublisher.publishEvent(
+					new StateChangeEvent<>(this, entity, stateName, changes.get(property), null));
+		}
+	}
+
+	@Override
+	public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
+		this.eventPublisher = applicationEventPublisher;
+	}
+}
-- 
GitLab