// Path: src/protocolsupportpocketstuff/libs/jodah/expiringmap/internal/Assert.java
// public final class Assert {
//   private Assert() {
//   }
// 
//   public static <T> T notNull(T reference, String parameterName) {
//     if (reference == null)
//       throw new NullPointerException(parameterName + " cannot be null");
//     return reference;
//   }
// 
//   public static void operation(boolean condition, String message) {
//     if (!condition)
//       throw new UnsupportedOperationException(message);
//   }
// 
//   public static void state(boolean expression, String errorMessageFormat, Object... args) {
//     if (!expression)
//       throw new IllegalStateException(String.format(errorMessageFormat, args));
//   }
// 
//   public static void element(Object element, Object key) {
//     if (element == null)
//       throw new NoSuchElementException(key.toString());
//   }
// }
// 
// Path: src/protocolsupportpocketstuff/libs/jodah/expiringmap/internal/NamedThreadFactory.java
// public class NamedThreadFactory implements ThreadFactory {
//   private final AtomicInteger threadNumber = new AtomicInteger(1);
//   private final String nameFormat;
// 
//   /**
//    * Creates a thread factory that names threads according to the {@code nameFormat} by supplying a
//    * single argument to the format representing the thread number.
//    */
//   public NamedThreadFactory(String nameFormat) {
//     this.nameFormat = nameFormat;
//   }
// 
//   @Override
//   public Thread newThread(Runnable r) {
//     Thread thread = new Thread(r, String.format(nameFormat, threadNumber.getAndIncrement()));
//     thread.setDaemon(true);
//     return thread;
//   }
// }


import protocolsupportpocketstuff.libs.jodah.expiringmap.internal.Assert;
import protocolsupportpocketstuff.libs.jodah.expiringmap.internal.NamedThreadFactory;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
package protocolsupportpocketstuff.libs.jodah.expiringmap;



/**
 * A thread-safe map that expires entries. Optional features include expiration policies, variable entry expiration,
 * lazy entry loading, and expiration listeners.
 * 
 * <p>
 * Entries are tracked by expiration time and expired by a single thread.
 * 
 * <p>
 * Expiration listeners are called synchronously as entries are expired and block write operations to the map until they
 * completed. Asynchronous expiration listeners are called on a separate thread pool and do not block map operations.
 * 
 * <p>
 * When variable expiration is disabled (default), put/remove operations have a time complexity <i>O(1)</i>. When
 * variable expiration is enabled, put/remove operations have time complexity of <i>O(log n)</i>.
 * 
 * <p>
 * Example usages:
 * 
 * <pre>
 * {@code 
 * Map<String, Integer> map = ExpiringMap.create(); 
 * Map<String, Integer> map = ExpiringMap.builder().expiration(30, TimeUnit.SECONDS).build();
 * Map<String, Connection> map = ExpiringMap.builder()
 *   .expiration(10, TimeUnit.MINUTES)
 *   .entryLoader(new EntryLoader<String, Connection>() {
 *     public Connection load(String address) {
 *       return new Connection(address);
 *     }
 *   })
 *   .expirationListener(new ExpirationListener<String, Connection>() { 
 *     public void expired(String key, Connection connection) { 
 *       connection.close();
 *     } 
 *   })
 *   .build();
 * }
 * </pre>
 * 
 * @author Jonathan Halterman
 * @param <K> Key type
 * @param <V> Value type
 */
public class ExpiringMap<K, V> implements ConcurrentMap<K, V> {
  static volatile ScheduledExecutorService EXPIRER;
  static volatile ThreadPoolExecutor LISTENER_SERVICE;
  static ThreadFactory THREAD_FACTORY;

  List<ExpirationListener<K, V>> expirationListeners;
  List<ExpirationListener<K, V>> asyncExpirationListeners;
  private AtomicLong expirationNanos;
  private int maxSize;
  private final AtomicReference<ExpirationPolicy> expirationPolicy;
  private final EntryLoader<? super K, ? extends V> entryLoader;
  private final ExpiringEntryLoader<? super K, ? extends V> expiringEntryLoader;
  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  private final Lock readLock = readWriteLock.readLock();
  private final Lock writeLock = readWriteLock.writeLock();
  /** Guarded by "readWriteLock" */
  private final EntryMap<K, V> entries;
  private final boolean variableExpiration;

  /**
   * Sets the {@link ThreadFactory} that is used to create expiration and listener callback threads for all ExpiringMap
   * instances.
   * 
   * @param threadFactory
   * @throws NullPointerException if {@code threadFactory} is null
   */
  public static void setThreadFactory(ThreadFactory threadFactory) {
// Path: src/protocolsupportpocketstuff/libs/jodah/expiringmap/internal/Assert.java
// public final class Assert {
//   private Assert() {
//   }
// 
//   public static <T> T notNull(T reference, String parameterName) {
//     if (reference == null)
//       throw new NullPointerException(parameterName + " cannot be null");
//     return reference;
//   }
// 
//   public static void operation(boolean condition, String message) {
//     if (!condition)
//       throw new UnsupportedOperationException(message);
//   }
// 
//   public static void state(boolean expression, String errorMessageFormat, Object... args) {
//     if (!expression)
//       throw new IllegalStateException(String.format(errorMessageFormat, args));
//   }
// 
//   public static void element(Object element, Object key) {
//     if (element == null)
//       throw new NoSuchElementException(key.toString());
//   }
// }
// 
// Path: src/protocolsupportpocketstuff/libs/jodah/expiringmap/internal/NamedThreadFactory.java
// public class NamedThreadFactory implements ThreadFactory {
//   private final AtomicInteger threadNumber = new AtomicInteger(1);
//   private final String nameFormat;
// 
//   /**
//    * Creates a thread factory that names threads according to the {@code nameFormat} by supplying a
//    * single argument to the format representing the thread number.
//    */
//   public NamedThreadFactory(String nameFormat) {
//     this.nameFormat = nameFormat;
//   }
// 
//   @Override
//   public Thread newThread(Runnable r) {
//     Thread thread = new Thread(r, String.format(nameFormat, threadNumber.getAndIncrement()));
//     thread.setDaemon(true);
//     return thread;
//   }
// }


// Path: src/protocolsupportpocketstuff/libs/jodah/expiringmap/ExpiringMap.java
import protocolsupportpocketstuff.libs.jodah.expiringmap.internal.Assert;
import protocolsupportpocketstuff.libs.jodah.expiringmap.internal.NamedThreadFactory;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

package protocolsupportpocketstuff.libs.jodah.expiringmap;



/**
 * A thread-safe map that expires entries. Optional features include expiration policies, variable entry expiration,
 * lazy entry loading, and expiration listeners.
 * 
 * <p>
 * Entries are tracked by expiration time and expired by a single thread.
 * 
 * <p>
 * Expiration listeners are called synchronously as entries are expired and block write operations to the map until they
 * completed. Asynchronous expiration listeners are called on a separate thread pool and do not block map operations.
 * 
 * <p>
 * When variable expiration is disabled (default), put/remove operations have a time complexity <i>O(1)</i>. When
 * variable expiration is enabled, put/remove operations have time complexity of <i>O(log n)</i>.
 * 
 * <p>
 * Example usages:
 * 
 * <pre>
 * {@code 
 * Map<String, Integer> map = ExpiringMap.create(); 
 * Map<String, Integer> map = ExpiringMap.builder().expiration(30, TimeUnit.SECONDS).build();
 * Map<String, Connection> map = ExpiringMap.builder()
 *   .expiration(10, TimeUnit.MINUTES)
 *   .entryLoader(new EntryLoader<String, Connection>() {
 *     public Connection load(String address) {
 *       return new Connection(address);
 *     }
 *   })
 *   .expirationListener(new ExpirationListener<String, Connection>() { 
 *     public void expired(String key, Connection connection) { 
 *       connection.close();
 *     } 
 *   })
 *   .build();
 * }
 * </pre>
 * 
 * @author Jonathan Halterman
 * @param <K> Key type
 * @param <V> Value type
 */
public class ExpiringMap<K, V> implements ConcurrentMap<K, V> {
  static volatile ScheduledExecutorService EXPIRER;
  static volatile ThreadPoolExecutor LISTENER_SERVICE;
  static ThreadFactory THREAD_FACTORY;

  List<ExpirationListener<K, V>> expirationListeners;
  List<ExpirationListener<K, V>> asyncExpirationListeners;
  private AtomicLong expirationNanos;
  private int maxSize;
  private final AtomicReference<ExpirationPolicy> expirationPolicy;
  private final EntryLoader<? super K, ? extends V> entryLoader;
  private final ExpiringEntryLoader<? super K, ? extends V> expiringEntryLoader;
  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  private final Lock readLock = readWriteLock.readLock();
  private final Lock writeLock = readWriteLock.writeLock();
  /** Guarded by "readWriteLock" */
  private final EntryMap<K, V> entries;
  private final boolean variableExpiration;

  /**
   * Sets the {@link ThreadFactory} that is used to create expiration and listener callback threads for all ExpiringMap
   * instances.
   * 
   * @param threadFactory
   * @throws NullPointerException if {@code threadFactory} is null
   */
  public static void setThreadFactory(ThreadFactory threadFactory) {
    THREAD_FACTORY = Assert.notNull(threadFactory, "threadFactory");