org.smallmind.javafx.extras.ConsolidatingEventHandler Maven / Gradle / Ivy
/*
* Copyright (c) 2007 through 2024 David Berkman
*
* This file is part of the SmallMind Code Project.
*
* The SmallMind Code Project is free software, you can redistribute
* it and/or modify it under either, at your discretion...
*
* 1) The terms of GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* ...or...
*
* 2) The terms of the Apache License, Version 2.0.
*
* The SmallMind Code Project 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 or Apache License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and the Apache License along with the SmallMind Code Project. If not, see
* or .
*
* Additional permission under the GNU Affero GPL version 3 section 7
* ------------------------------------------------------------------
* If you modify this Program, or any covered work, by linking or
* combining it with other code, such other code is not for that reason
* alone subject to any of the requirements of the GNU Affero GPL
* version 3.
*/
package org.smallmind.javafx.extras;
import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javafx.event.Event;
import javafx.event.EventHandler;
import jfxtras.util.PlatformUtil;
import org.smallmind.scribe.pen.LoggerManager;
public class ConsolidatingEventHandler implements EventHandler, Comparable> {
private static final CountDownLatch stopLatch = new CountDownLatch(1);
private static final ConcurrentSkipListMap LOOSE_EVENT_MAP = new ConcurrentSkipListMap<>();
private final EventHandler innerEventHandler;
private final long consolidationTimeMillis;
private int generation;
static {
Thread thread = new Thread(new ConsolidationWorker());
thread.setDaemon(true);
thread.start();
}
public ConsolidatingEventHandler (long consolidationTimeMillis, EventHandler innerEventHandler) {
this.consolidationTimeMillis = consolidationTimeMillis;
this.innerEventHandler = innerEventHandler;
}
private EventHandler getInnerEventHandler () {
return innerEventHandler;
}
private synchronized int getGeneration () {
return generation;
}
@Override
public synchronized final void handle (T event) {
LOOSE_EVENT_MAP.put(new ConsolidatingKey<>(this, ++generation, consolidationTimeMillis), event);
}
@Override
public int compareTo (ConsolidatingEventHandler> handler) {
return hashCode() - handler.hashCode();
}
private static class ConsolidationWorker implements Runnable {
@Override
public void run () {
try {
while (!stopLatch.await(50, TimeUnit.MILLISECONDS)) {
NavigableMap expiredKeyMap;
if (!(expiredKeyMap = LOOSE_EVENT_MAP.headMap(new ConsolidatingKey())).isEmpty()) {
Map.Entry entry;
while ((entry = expiredKeyMap.pollFirstEntry()) != null) {
synchronized (entry.getKey().getEventHandler()) {
if (entry.getKey().getGeneration() == entry.getKey().getEventHandler().getGeneration()) {
final ConsolidatingKey key = entry.getKey();
final Event event = entry.getValue();
PlatformUtil.runAndWait(new Runnable() {
@Override
public void run () {
key.getEventHandler().getInnerEventHandler().handle(event);
}
});
}
}
}
}
}
} catch (InterruptedException interruptedException) {
LoggerManager.getLogger(ConsolidatingChangeListener.class).error(interruptedException);
}
}
}
private static class ConsolidatingKey implements Comparable> {
private final ConsolidatingEventHandler handler;
private final long expiration;
private final int generation;
private ConsolidatingKey () {
this(null, 0, 0);
}
private ConsolidatingKey (ConsolidatingEventHandler handler, int generation, long consolidationTimeMillis) {
this.handler = handler;
this.generation = generation;
expiration = System.currentTimeMillis() + consolidationTimeMillis;
}
private ConsolidatingEventHandler> getEventHandler () {
return handler;
}
private int getGeneration () {
return generation;
}
private long getExpiration () {
return expiration;
}
@Override
public int compareTo (ConsolidatingKey key) {
int comparison;
if ((comparison = Long.compare(expiration, key.getExpiration())) == 0) {
return (handler == null) ? ((key.getEventHandler() == null) ? 0 : -1) : ((key.getEventHandler() == null) ? 1 : handler.compareTo(key.getEventHandler()));
}
return comparison;
}
}
}