Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: d9fd2ed293e21339775ecc0d14f822d77fba4376 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                            






                                                  
                                        




                                              









                                                                                                                           




























                                                                                                      
                 





                          
                 





                                        


                                            
                            
       
                      






                     











                                                          







                          
























































































                                                                          
                 







                                        
                   





























                                                                






                        






























































































































































































                                                                                                   

                                     







                             

















                                                         



                             















                                             










                                                           
                       


     
/***************************************************************************
 * Copyright (c) 2004 - 2007 Eike Stepper, Germany.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *    Eike Stepper - initial API and implementation
 **************************************************************************/
package org.eclipse.net4j.util.ref;

import org.eclipse.net4j.util.collection.MapEntry;

import java.lang.ref.ReferenceQueue;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * A {@link ConcurrentMap} implementation that uses {@link KeyedReference} instances ({@link KeyedReference#Strong Strong},
 * {@link KeyedReference#Soft Soft}, {@link KeyedReference#Weak Weak} or {@link KeyedReference#Phantom Phantom}) as
 * its values.
 * <p>
 * A <code>ReferenceValueMap</code> can be used to cache mappings until the <em>value</em> of the mapping is no
 * longer reachable from outside of the map
 * <p>
 * <b>Note:</b> This map is not synchronized. If it is to be used by multiple threads concurrently the user is
 * responsible for applying proper external synchronization!
 * 
 * @author Eike Stepper
 */
public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
{
  private ConcurrentMap<K, KeyedReference<K, V>> map;

  private ReferenceQueue<V> queue;

  private EntrySet entrySet;

  public ReferenceValueMap()
  {
    this(new ConcurrentHashMap<K, KeyedReference<K, V>>());
  }

  public ReferenceValueMap(ConcurrentMap<K, KeyedReference<K, V>> map)
  {
    if (!map.isEmpty())
    {
      throw new IllegalArgumentException("!map.isEmpty()");
    }

    this.map = map;
    queue = createQueue();
  }

  @Override
  public int size()
  {
    purgeQueue();
    return map.size();
  }

  @Override
  public boolean isEmpty()
  {
    purgeQueue();
    return map.isEmpty();
  }

  @Override
  public boolean containsKey(Object key)
  {
    KeyedReference<K, V> ref = map.get(key);
    if (ref != null)
    {
      if (ref.get() == null)
      {
        ref.enqueue();
        return false;
      }

      return true;
    }

    return false;
  }

  @Override
  public boolean containsValue(Object value)
  {
    if (value == null)
    {
      throw new IllegalArgumentException("value == null");
    }

    for (KeyedReference<K, V> ref : map.values())
    {
      V v = ref.get();
      if (v == null)
      {
        ref.enqueue();
        return false;
      }

      if (value.equals(v))
      {
        return true;
      }
    }

    return false;
  }

  @Override
  public V get(Object key)
  {
    KeyedReference<K, V> ref = map.get(key);
    return dereference(ref);
  }

  @Override
  public V put(K key, V value)
  {
    try
    {
      KeyedReference<K, V> ref = createReference(key, value, queue);
      KeyedReference<K, V> oldRef = map.put(key, ref);
      return dereference(oldRef);
    }
    finally
    {
      purgeQueue();
    }
  }

  public V putIfAbsent(K key, V value)
  {
    try
    {
      KeyedReference<K, V> ref = createReference(key, value, queue);
      KeyedReference<K, V> oldRef = map.putIfAbsent(key, ref);
      return dereference(oldRef);
    }
    finally
    {
      purgeQueue();
    }
  }

  public V replace(K key, V value)
  {
    try
    {
      KeyedReference<K, V> ref = createReference(key, value, queue);
      KeyedReference<K, V> oldRef = map.replace(key, ref);
      return dereference(oldRef);
    }
    finally
    {
      purgeQueue();
    }
  }

  public boolean replace(K key, V oldValue, V newValue)
  {
    try
    {
      // TODO Consider a dummy KeyedReference class for oldRef
      KeyedReference<K, V> oldRef = createReference(key, oldValue, queue);
      KeyedReference<K, V> newRef = createReference(key, newValue, queue);
      return map.replace(key, oldRef, newRef);
    }
    finally
    {
      purgeQueue();
    }
  }

  @Override
  public V remove(Object key)
  {
    KeyedReference<K, V> ref = map.remove(key);
    return dereference(ref);
  }

  public boolean remove(Object key, Object value)
  {
    // TODO Consider a dummy KeyedReference class for value
    return map.remove(key, value);
  }

  @Override
  public void clear()
  {
    purgeQueue();
    map.clear();
  }

  @Override
  public Set<Map.Entry<K, V>> entrySet()
  {
    if (entrySet == null)
    {
      purgeQueue();
      entrySet = new EntrySet();
    }

    return entrySet;
  }

  protected ReferenceQueue<V> createQueue()
  {
    return new ReferenceQueue<V>();
  }

  protected void purgeQueue()
  {
    if (queue != null)
    {
      KeyedReference<K, V> ref;
      while ((ref = (KeyedReference<K, V>)queue.poll()) != null)
      {
        map.remove(ref.getKey());
      }
    }
  }

  protected V dereference(KeyedReference<K, V> ref)
  {
    if (ref == null)
    {
      return null;
    }

    V value = ref.get();
    if (value == null)
    {
      ref.enqueue();
    }

    return value;
  }

  protected abstract KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue);

  /**
   * @author Eike Stepper
   */
  public static class Strong<K, V> extends ReferenceValueMap<K, V>
  {
    public Strong()
    {
    }

    public Strong(ConcurrentMap<K, KeyedReference<K, V>> map)
    {
      super(map);
    }

    @Override
    protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
    {
      return new KeyedStrongReference<K, V>(key, value);
    }

    @Override
    protected ReferenceQueue<V> createQueue()
    {
      return null;
    }
  }

  /**
   * @author Eike Stepper
   */
  public static class Soft<K, V> extends ReferenceValueMap<K, V>
  {
    public Soft()
    {
    }

    public Soft(ConcurrentMap<K, KeyedReference<K, V>> map)
    {
      super(map);
    }

    @Override
    protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
    {
      return new KeyedSoftReference<K, V>(key, value, queue);
    }
  }

  /**
   * @author Eike Stepper
   */
  public static class Weak<K, V> extends ReferenceValueMap<K, V>
  {
    public Weak()
    {
    }

    public Weak(ConcurrentMap<K, KeyedReference<K, V>> map)
    {
      super(map);
    }

    @Override
    protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
    {
      return new KeyedWeakReference<K, V>(key, value, queue);
    }
  }

  /**
   * @author Eike Stepper
   */
  private class EntrySet extends AbstractSet<Map.Entry<K, V>>
  {
    public EntrySet()
    {
    }

    @Override
    public int size()
    {
      return map.size();
    }

    @Override
    public boolean isEmpty()
    {
      return map.isEmpty();
    }

    @Override
    public boolean contains(Object object)
    {
      if (object == null)
      {
        throw new IllegalArgumentException("object == null");
      }

      if (object instanceof Map.Entry<?, ?>)
      {
        Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
        Object key = entry.getKey();
        Object value = entry.getValue();
        return key != null && value != null && value.equals(get(key));
      }

      return false;
    }

    @Override
    public Iterator<Map.Entry<K, V>> iterator()
    {
      return new EntrySetIterator();
    }

    @Override
    public Object[] toArray()
    {
      Object[] a = new Object[size()];
      int i = 0;
      for (Map.Entry<K, V> entry : this)
      {
        a[i++] = entry;
      }

      return a;
    }

    @Override
    public <T> T[] toArray(T[] a)
    {
      if (a == null)
      {
        throw new IllegalArgumentException("array == null");
      }

      int size = size();
      if (a.length < size)
      {
        a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
      }

      int i = 0;
      for (Map.Entry<K, V> entry : this)
      {
        a[i++] = (T)entry;
      }

      if (a.length > size)
      {
        a[size] = null;
      }

      return a;
    }

    @Override
    public boolean remove(Object object)
    {
      if (object == null)
      {
        throw new IllegalArgumentException("object == null");
      }

      if (object instanceof Map.Entry<?, ?>)
      {
        Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
        return map.remove(entry.getKey(), entry.getValue());
      }

      return false;
    }

    @Override
    public void clear()
    {
      map.clear();
    }
  }

  /**
   * @author Eike Stepper
   */
  private class EntrySetIterator implements Iterator<Map.Entry<K, V>>
  {
    private Iterator<Entry<K, KeyedReference<K, V>>> it = map.entrySet().iterator();

    private MapEntry<K, V> nextEntry;

    private K lastKey;

    public EntrySetIterator()
    {
    }

    public boolean hasNext()
    {
      if (nextEntry != null)
      {
        return true;
      }

      while (it.hasNext())
      {
        Entry<K, KeyedReference<K, V>> entry = it.next();
        lastKey = entry.getKey();
        V value = dereference(entry.getValue());
        if (value != null)
        {
          nextEntry = new MapEntry<K, V>(lastKey, value);
          return true;
        }
      }

      return false;
    }

    public Entry<K, V> next()
    {
      if (nextEntry == null)
      {
        if (!hasNext())
        {
          throw new NoSuchElementException();
        }
      }

      try
      {
        return nextEntry;
      }
      finally
      {
        nextEntry = null;
      }
    }

    public void remove()
    {
      if (lastKey == null)
      {
        throw new IllegalStateException("lastKey == null");
      }

      map.remove(lastKey);
      lastKey = null;
      nextEntry = null;
    }
  }
}

Back to the top