<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.ki.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.*;


/**
 * A <code><em>Soft</em>HashMap</code> is a memory-constrained map that stores its <em>values</em> in
 * {@link SoftReference SoftReference}s.  (Contrast this with the JDK's
 * {@link WeakHashMap WeakHashMap}, which uses weak references for its <em>keys</em>, which is of little value if you
 * want the cache to auto-resize itself based on memory constraints).
 * <p/>
 * Having the values wrapped by soft references allows the cache to automatically reduce its size based on memory
 * limitations and garbage collection.  This ensures that the cache will not cause memory leaks by holding hard
 * references to all of its values.
 * <p/>
 * This class is a generics-enabled Map based on initial ideas from <NAME>'s and Sydney Redelinghuys's
 * <a href="http://www.javaspecialists.eu/archive/Issue015.html">publicly posted version</a>, with continued
 * modifications.
 *
 * @author <NAME>
 * @since 1.0
 */
public class SoftHashMap<K, V> extends AbstractMap<K, V> {

    /** The default value of the HARD_SIZE attribute, equal to 100. */
    private static final int DEFAULT_HARD_SIZE = 100;

    /** The internal HashMap that will hold the SoftReference. */
    private final Map<K, SoftValue<V, K>> map;

    /**
     * The number of &quot;hard&quot; references to hold internally, that is, the number of instances to prevent
     * from being garbage collected automatically (unlike other soft references).
     */
    private final int HARD_SIZE;

    /** The FIFO list of hard references (not to be garbage collected), order of last access. */
    protected final Collection<V> hardCache;
    private int hardCacheSize = 0;

    /** Reference queue for cleared SoftReference objects. */
    private final ReferenceQueue<? super V> queue = new ReferenceQueue<V>();

    public SoftHashMap() {
        this(DEFAULT_HARD_SIZE);
    }

    @SuppressWarnings({"unchecked"})
    public SoftHashMap(int hardSize) {
        super();
        HARD_SIZE = hardSize;
        map = createSoftReferenceMap();
        hardCache = createHardCache();
    }

    protected Map<K, SoftValue<V, K>> createSoftReferenceMap() {
        Map<K, SoftValue<V, K>> map;
        if (JavaEnvironment.isAtLeastVersion15()) {
            map = new java.util.concurrent.ConcurrentHashMap<K, SoftValue<V, K>>();
        } else {
            map = (Map) ClassUtils.newInstance("edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap");
        }
        return map;
    }

    @SuppressWarnings({"unchecked"})
    protected Collection<V> createHardCache() {
        Collection<V> c;
        if (JavaEnvironment.isAtLeastVersion15()) {
            c = new java.util.concurrent.ConcurrentLinkedQueue<V>();
        } else {
            c = (Collection) ClassUtils.newInstance("edu.emory.mathcs.backport.java.util.concurrent.ConcurrentLinkedQueue");
        }
        return c;
    }

    protected V pollQueue(Collection<V> queue) {
        return ((Queue<V>) queue).poll();
    }

    public V get(Object key) {
        V result = null;
        SoftValue<V, K> value = map.get(key);

        if (value != null) {
            //unwrap the 'real' value from the SoftReference
            result = value.get();
            if (result == null) {
                //The wrapped value was garbage collected, so remove this entry from the backing map:
                map.remove(key);
            } else {
                //Add this value to the beginning of the 'hard' reference queue (FIFO).
                addToHardCache(result);
                trimHardCacheIfNecessary();
            }
        }
        return result;
    }

    protected void addToHardCache(V result) {
        hardCache.add(result);
        hardCacheSize++;
    }

    protected void trimHardCacheIfNecessary() {
        //trim the hard ref queue if necessary:
        V trimmed = null;
        if (hardCacheSize > HARD_SIZE) {
            trimmed = pollHardCache();
        }
        if (trimmed != null) {
            hardCacheSize--;
        }
    }

    protected V pollHardCache() {
        V polled = null;
        if (JavaEnvironment.isAtLeastVersion15() && hardCache instanceof Queue) {
            polled = ((Queue<V>) hardCache).poll();
        } else {
            Iterator<V> i = hardCache.iterator();
            if (i.hasNext()) {
                polled = i.next();
                i.remove();
            }
        }
        if (polled != null) {
            hardCacheSize--;
        }
        return polled;
    }


    /**
     * Traverses the ReferenceQueue and removes garbage-collected SoftValue objects from the backing map
     * by looking them up using the SoftValue.key data member.
     */
    private void processQueue() {
        SoftValue sv;
        while ((sv = (SoftValue) queue.poll()) != null) {
            map.remove(sv.key); // we can access private data!
        }
    }

    /** Creates a new entry, but wraps the value in a SoftValue instance to enable auto garbage collection. */
    public V put(K key, V value) {
        processQueue(); // throw out garbage collected values first
        SoftValue<V, K> sv = new SoftValue<V, K>(value, key, queue);
        SoftValue<V, K> previous = map.put(key, sv);
        return previous != null ? previous.get() : null;
    }

    public V remove(Object key) {
        processQueue(); // throw out garbage collected values first
        SoftValue<V, K> raw = map.remove(key);
        return raw != null ? raw.get() : null;
    }

    public void clear() {
        hardCache.clear();
        processQueue(); // throw out garbage collected values
        map.clear();
    }

    public int size() {
        processQueue(); // throw out garbage collected values first
        return map.size();
    }

    @SuppressWarnings({"unchecked"})
    public Set<Map.Entry<K, V>> entrySet() {
        processQueue(); // throw out garbage collected values first
        Set set = map.entrySet();
        return Collections.unmodifiableSet(set);
    }

    /**
     * We define our own subclass of SoftReference which contains
     * not only the value but also the key to make it easier to find
     * the entry in the HashMap after it's been garbage collected.
     */
    private static class SoftValue<V, K> extends SoftReference<V> {

        private final K key;

        /**
         * Constructs a new instance, wrapping the value, key, and queue, as
         * required by the superclass.
         *
         * @param value the map value
         * @param key   the map key
         * @param queue the soft reference queue to poll to determine if the entry had been reaped by the GC.
         */
        private SoftValue(V value, K key, ReferenceQueue<? super V> queue) {
            super(value, queue);<commit_msg>javadoc & minor enhancements (implemented all methods in the Map API instead of subclassing AbstractMap)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.ki.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;


/**
 * A <code><em>Soft</em>HashMap</code> is a memory-constrained map that stores its <em>values</em> in
 * {@link SoftReference SoftReference}s.  (Contrast this with the JDK's
 * {@link WeakHashMap WeakHashMap}, which uses weak references for its <em>keys</em>, which is of little value if you
 * want the cache to auto-resize itself based on memory constraints).
 * <p/>
 * Having the values wrapped by soft references allows the cache to automatically reduce its size based on memory
 * limitations and garbage collection.  This ensures that the cache will not cause memory leaks by holding strong
 * references to all of its values.
 * <p/>
 * This class is a generics-enabled Map based on initial ideas from <NAME>'s and Sydney Redelinghuys's
 * <a href="http://www.javaspecialists.eu/archive/Issue015.html">publicly posted version</a>, with continued
 * modifications.
 * <p/>
 * This implementation is thread-safe and usuable in highly concurrent environments.
 *
 * @author <NAME>
 * @since 1.0
 */
public class SoftHashMap<K, V> implements Map<K, V> {

    /** The default value of the RETENTION_SIZE attribute, equal to 100. */
    private static final int DEFAULT_RETENTION_SIZE = 100;

    /** The internal HashMap that will hold the SoftReference. */
    private final Map<K, SoftValue<V, K>> map;

    /**
     * The number of strong references to hold internally, that is, the number of instances to prevent
     * from being garbage collected automatically (unlike other soft references).
     */
    private final int RETENTION_SIZE;

    /** The FIFO list of strong references (not to be garbage collected), order of last access. */
    protected final Queue<V> strongReferences;
    private int strongReferenceCount = 0;

    /** Reference queue for cleared SoftReference objects. */
    private final ReferenceQueue<? super V> queue = new ReferenceQueue<V>();

    /**
     * Creates a new SoftHashMap with a default retention size size of
     * {@link #DEFAULT_RETENTION_SIZE DEFAULT_RETENTION_SIZE} (100 entries).
     *
     * @see #SoftHashMap(int)
     */
    public SoftHashMap() {
        this(DEFAULT_RETENTION_SIZE);
    }

    /**
     * Creates a new SoftHashMap with the specified retention size.
     * <p/>
     * The retention size (n) is the total number of most recent entries in the map that will be strongly referenced
     * (ie 'retained') to prevent them from being eagerly garbage collected.  That is, the point of a SoftHashMap is to
     * allow the garbage collector to remove as many entries from this map as it desires, but there will always be (n)
     * elements retained after a GC due to the strong references.
     * <p/>
     * Note that in a highly concurrent environments the exact total number of strong references may differ slightly
     * than the actual <code>retentionSize</code> value.  This number is intended to be a best-effort retention low
     * water mark.
     *
     * @param retentionSize the total number of most recent entries in the map that will be strongly referenced
     *                      (retained), preventing them from being eagerly garbage collected by the JVM.
     */
    @SuppressWarnings({"unchecked"})
    public SoftHashMap(int retentionSize) {
        super();
        RETENTION_SIZE = Math.max(0, retentionSize);
        map = createSoftReferenceMap();
        strongReferences = createStrongReferenceCache();
    }

    /**
     * Creates a {@code SoftHashMap} backed by the specified {@code source}, with a default retention
     * size of {@link #DEFAULT_RETENTION_SIZE DEFAULT_RETENTION_SIZE} (100 entries).
     *
     * @param source the backing map to populate this {@code SoftHashMap}
     * @see #SoftHashMap(Map,int)
     */
    public SoftHashMap(Map<K, V> source) {
        this(DEFAULT_RETENTION_SIZE);
        putAll(source);
    }

    /**
     * Creates a {@code SoftHashMap} backed by the specified {@code source}, with the specified retention size.
     * <p/>
     * The retention size (n) is the total number of most recent entries in the map that will be strongly referenced
     * (ie 'retained') to prevent them from being eagerly garbage collected.  That is, the point of a SoftHashMap is to
     * allow the garbage collector to remove as many entries from this map as it desires, but there will always be (n)
     * elements retained after a GC due to the strong references.
     * <p/>
     * Note that in a highly concurrent environments the exact total number of strong references may differ slightly
     * than the actual <code>retentionSize</code> value.  This number is intended to be a best-effort retention low
     * water mark.
     *
     * @param source        the backing map to populate this {@code SoftHashMap}
     * @param retentionSize the total number of most recent entries in the map that will be strongly referenced
     *                      (retained), preventing them from being eagerly garbage collected by the JVM.
     */
    public SoftHashMap(Map<K, V> source, int retentionSize) {
        this(retentionSize);
        putAll(source);
    }

    protected Map<K, SoftValue<V, K>> createSoftReferenceMap() {
        return new ConcurrentHashMap<K, SoftValue<V, K>>();
    }

    @SuppressWarnings({"unchecked"})
    protected Queue<V> createStrongReferenceCache() {
        return new ConcurrentLinkedQueue<V>();
    }

    public V get(Object key) {
        processQueue();

        V result = null;
        SoftValue<V, K> value = map.get(key);

        if (value != null) {
            //unwrap the 'real' value from the SoftReference
            result = value.get();
            if (result == null) {
                //The wrapped value was garbage collected, so remove this entry from the backing map:
                map.remove(key);
            } else {
                //Add this value to the beginning of the strong reference queue (FIFO).
                addToStrongReferences(result);
                trimStrongReferencesIfNecessary();
            }
        }
        return result;
    }

    protected void addToStrongReferences(V result) {
        strongReferences.add(result);
        strongReferenceCount++;
    }

    protected void trimStrongReferencesIfNecessary() {
        //trim the strong ref queue if necessary:
        while (strongReferenceCount > RETENTION_SIZE) {
            pollStrongReferences();
        }
    }

    protected V pollStrongReferences() {
        V polled = strongReferences.poll();
        if (polled != null) {
            strongReferenceCount--;
        }
        return polled;
    }

    /**
     * Traverses the ReferenceQueue and removes garbage-collected SoftValue objects from the backing map
     * by looking them up using the SoftValue.key data member.
     */
    private void processQueue() {
        SoftValue sv;
        while ((sv = (SoftValue) queue.poll()) != null) {
            map.remove(sv.key); // we can access private data!
        }
    }

    public boolean isEmpty() {
        processQueue();
        return map.isEmpty();
    }

    public boolean containsKey(Object key) {
        processQueue();
        return map.containsKey(key);
    }

    public boolean containsValue(Object value) {
        processQueue();
        Collection values = values();
        return values != null && values.contains(value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        if (m == null || m.isEmpty()) {
            processQueue();
            return;
        }
        for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    public Set<K> keySet() {
        processQueue();
        return map.keySet();
    }

    @SuppressWarnings({"unchecked"})
    public Collection<V> values() {
        processQueue();
        if (map.isEmpty()) {
            return Collections.EMPTY_SET;
        }
        Collection<K> keys = map.keySet();
        Collection<V> values = new ArrayList<V>(map.values().size());
        for (K key : keys) {
            V v = get(key);
            if (v != null) {
                values.add(v);
            }
        }
        return values;
    }

    /** Creates a new entry, but wraps the value in a SoftValue instance to enable auto garbage collection. */
    public V put(K key, V value) {
        processQueue(); // throw out garbage collected values first
        SoftValue<V, K> sv = new SoftValue<V, K>(value, key, queue);
        SoftValue<V, K> previous = map.put(key, sv);
        return previous != null ? previous.get() : null;
    }

    public V remove(Object key) {
        processQueue(); // throw out garbage collected values first
        SoftValue<V, K> raw = map.remove(key);
        return raw != null ? raw.get() : null;
    }

    public void clear() {
        strongReferences.clear();
        processQueue(); // throw out garbage collected values
        map.clear();
    }

    public int size() {
        processQueue(); // throw out garbage collected values first
        return map.size();
    }

    @SuppressWarnings({"unchecked"})
    public Set<Map.Entry<K, V>> entrySet() {
        processQueue(); // throw out garbage collected values first
        if (map.isEmpty()) {
            return Collections.EMPTY_SET;
        }

        Map kvPairs = new HashMap(map.size());
        Collection<K> keys = map.keySet();
        for (K key : keys) {
            V v = get(key);
            if (v != null) {
                kvPairs.put(key, v);
            }
        }
        return kvPairs.entrySet();
    }

    /**
     * We define our own subclass of SoftReference which contains
     * not only the value but also the key to make it easier to find
     * the entry in the HashMap after it's been garbage collected.
     */
    private static class SoftValue<V, K> extends SoftReference<V> {

        private final K key;

        /**
         * Constructs a new instance, wrapping the value, key, and queue, as
         * required by the superclass.
         *
         * @param value the map value
         * @param key   the map key
         * @param queue the soft reference queue to poll to determine if the entry had been reaped by the GC.
         */
        private SoftValue(V value, K key, ReferenceQueue<? super V> queue) {
            super(value, queue);
{'fraction_non_alphanumeric': 0.09392092514455384, 'fraction_numerical': 0.0023441162681669013, 'mean_word_length': 2.478528718970828, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 105, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7245268', 'n_tokens_mistral': 5133, 'n_tokens_neox': 4976, 'n_words': 1962}