1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal;
  27 
  28 import java.lang.ref.ReferenceQueue;
  29 import java.lang.ref.WeakReference;
  30 import java.util.HashMap;
  31 import java.util.function.Function;
  32 
  33 /**
  34  * This class provides a map based cache with weakly referenced values. Cleared references are
  35  * purged from the underlying map when values are retrieved or created.
  36  * It uses a {@link java.util.HashMap} to store values and needs to be externally synchronized.
  37  *
  38  * @param <K> the key type
  39  * @param <V> the value type
  40  */
  41 public final class WeakValueCache<K, V> {
  42 
  43     private final HashMap<K, KeyValueReference<K, V>> map = new HashMap<>();
  44     private final ReferenceQueue<V> refQueue = new ReferenceQueue<>();
  45 
  46     /**
  47      * Returns the value associated with {@code key}, or {@code null} if no such value exists.
  48      *
  49      * @param key the key
  50      * @return the value or null if none exists
  51      */
  52     public V get(final K key) {
  53         // Remove cleared entries
  54         for (;;) {
  55             final KeyValueReference<?, ?> ref = (KeyValueReference) refQueue.poll();
  56             if (ref == null) {
  57                 break;
  58             }
  59             map.remove(ref.key, ref);
  60         }
  61 
  62         final KeyValueReference<K, V> ref = map.get(key);
  63         if (ref != null) {
  64             return ref.get();
  65         }
  66         return null;
  67     }
  68 
  69     /**
  70      * Returns the value associated with {@code key}, or creates and returns a new value if
  71      * no value exists using the {@code creator} function.
  72      *
  73      * @param key the key
  74      * @param creator function to create a new value
  75      * @return the existing value, or a new one if none existed
  76      */
  77     public V getOrCreate(final K key, final Function<? super K, ? extends V> creator) {
  78         V value = get(key);
  79 
  80         if (value == null) {
  81             // Define a new value if it does not exist
  82             value = creator.apply(key);
  83             map.put(key, new KeyValueReference<>(key, value));
  84         }
  85 
  86         return value;
  87     }
  88 
  89     private static class KeyValueReference<K, V> extends WeakReference<V> {
  90         final K key;
  91 
  92         KeyValueReference(final K key, final V value) {
  93             super(value);
  94             this.key = key;
  95         }
  96     }
  97 
  98 }