content: /*
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package io.airlift.concurrent;
  
  import java.util.LinkedHashMap;
  import java.util.Map;
  import java.util.function.Function;
  
  import static com.google.common.base.Preconditions.checkArgument;
  import static java.util.Objects.requireNonNull;
  
  /**
   * Provides a ThreadLocal cache with a maximum cache size per thread.
   * Values must not be null.
   *
   * @param <K> cache key type
   * @param <V> cache value type
   */
  public final class ThreadLocalCache<K, V>
  {
      @SuppressWarnings("ThreadLocalNotStaticFinal")
      private final ThreadLocal<Map<K, V>> cache;
      private final Function<K, V> loader;
  
      public ThreadLocalCache(int maxSizePerThread, Function<K, V> loader)
      {
          checkArgument(maxSizePerThread > 0, "max size must be greater than zero");
          this.cache = ThreadLocal.withInitial(() -> new BoundedMap<K, V>(maxSizePerThread));
          this.loader = requireNonNull(loader, "loader is null");
      }
  
      public V get(K key)
      {
          V value = cache.get().computeIfAbsent(key, loader);
          return requireNonNull(value, "loader returned null value");
      }
  
      @SuppressWarnings("CloneableClassWithoutClone")
      private static class BoundedMap<K, V>
              extends LinkedHashMap<K, V>
      {
          private final int maxSize;
  
          public BoundedMap(int maxSize)
          {
              this.maxSize = maxSize;
          }
  
          @Override
          protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
          {
              return size() > maxSize;
          }
      }
  }
  