All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.smallmind.javafx.extras.ConsolidatingEventHandler Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * 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;
    }
  }
}