| /* |
| * Copyright (c) 2016 Mockito contributors |
| * This program is made available under the terms of the MIT License. |
| */ |
| package org.mockito.internal.util.concurrent; |
| |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| /** |
| * <p> |
| * A thread-safe set with weak values. Entries are based on a key's system hash code and keys are considered equal only by reference equality. |
| * </p> |
| * This class does not implement the {@link java.util.Set} interface because this implementation is incompatible |
| * with the set contract. While iterating over a set's entries, any value that has not passed iteration is referenced non-weakly. |
| */ |
| public class WeakConcurrentSet<V> implements Runnable, Iterable<V> { |
| |
| final WeakConcurrentMap<V, Boolean> target; |
| |
| public WeakConcurrentSet(Cleaner cleaner) { |
| switch (cleaner) { |
| case INLINE: |
| target = new WeakConcurrentMap.WithInlinedExpunction<V, Boolean>(); |
| break; |
| case THREAD: |
| case MANUAL: |
| target = new WeakConcurrentMap<V, Boolean>(cleaner == Cleaner.THREAD); |
| break; |
| default: |
| throw new AssertionError(); |
| } |
| } |
| |
| /** |
| * @param value The value to add to the set. |
| * @return {@code true} if the value was added to the set and was not contained before. |
| */ |
| public boolean add(V value) { |
| return target.put(value, Boolean.TRUE) == null; // is null or Boolean.TRUE |
| } |
| |
| /** |
| * @param value The value to check if it is contained in the set. |
| * @return {@code true} if the set contains the value. |
| */ |
| public boolean contains(V value) { |
| return target.containsKey(value); |
| } |
| |
| /** |
| * @param value The value to remove from the set. |
| * @return {@code true} if the value is contained in the set. |
| */ |
| public boolean remove(V value) { |
| return target.remove(value); |
| } |
| |
| /** |
| * Clears the set. |
| */ |
| public void clear() { |
| target.clear(); |
| } |
| |
| /** |
| * Returns the approximate size of this set where the returned number is at least as big as the actual number of entries. |
| * |
| * @return The minimum size of this set. |
| */ |
| public int approximateSize() { |
| return target.approximateSize(); |
| } |
| |
| @Override |
| public void run() { |
| target.run(); |
| } |
| |
| /** |
| * Determines the cleaning format. A reference is removed either by an explicitly started cleaner thread |
| * associated with this instance ({@link Cleaner#THREAD}), as a result of interacting with this thread local |
| * from any thread ({@link Cleaner#INLINE} or manually by submitting the detached thread local to a thread |
| * ({@link Cleaner#MANUAL}). |
| */ |
| public enum Cleaner { |
| THREAD, INLINE, MANUAL |
| } |
| |
| /** |
| * Cleans all unused references. |
| */ |
| public void expungeStaleEntries() { |
| target.expungeStaleEntries(); |
| } |
| |
| /** |
| * @return The cleaner thread or {@code null} if no such thread was set. |
| */ |
| public Thread getCleanerThread() { |
| return target.getCleanerThread(); |
| } |
| |
| @Override |
| public Iterator<V> iterator() { |
| return new ReducingIterator<V>(target.iterator()); |
| } |
| |
| private static class ReducingIterator<V> implements Iterator<V> { |
| |
| private final Iterator<Map.Entry<V, Boolean>> iterator; |
| |
| private ReducingIterator(Iterator<Map.Entry<V, Boolean>> iterator) { |
| this.iterator = iterator; |
| } |
| |
| @Override |
| public void remove() { |
| iterator.remove(); |
| } |
| |
| @Override |
| public V next() { |
| return iterator.next().getKey(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return iterator.hasNext(); |
| } |
| } |
| } |