// -----------------------------------------------------------------------
//
//
// 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.
//
//
// -----------------------------------------------------------------------
namespace Lucene.Net.Support
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
///
/// A dictionary of weak references.
///
///
///
///
/// C# File:
/// src/Lucene.Net/Support/WeakDictionaryOfTKeyTValue.cs
///
///
///
/// C# Tests:
/// test/Lucene.Net.Test/Support/WeakDictionaryOfTKeyTValueTest.cs
///
///
///
///
/// Heavily base on the implementation found here on
/// Nick Guerrera's Blog
///
///
/// This was implemented to be the C# equivalent of has a Java WeakHashMap.
///
///
/// The TKey type.
/// The TValue type.
public sealed class WeakDictionary : BaseDictionary
where TKey : class
where TValue : class
{
private Dictionary> internalDictionary;
private WeakKeyComparer comparer;
private int initialCapacity = 0;
private DateTime lastRemoval = DateTime.Now;
///
/// Initializes a new instance of the class.
///
public WeakDictionary()
: this(0, null)
{
}
///
/// Initializes a new instance of the class.
///
/// The capacity.
public WeakDictionary(int capacity)
: this(capacity, null)
{
}
///
/// Initializes a new instance of the class.
///
/// The comparer.
public WeakDictionary(IEqualityComparer comparer)
: this(0, comparer)
{
}
///
/// Initializes a new instance of the class.
///
/// The capacity.
/// The comparer.
public WeakDictionary(int capacity, IEqualityComparer comparer)
{
this.PeriodicRemoval = new TimeSpan(0, 0, 15);
this.initialCapacity = capacity;
this.comparer = new WeakKeyComparer(comparer);
this.internalDictionary = new Dictionary>(capacity, this.comparer);
}
///
/// Initializes a new instance of the class.
///
/// The dictionary.
public WeakDictionary(IDictionary dictionary)
: this(0, null)
{
foreach (var pair in dictionary)
this.Add(pair.Key, pair.Value);
}
///
/// Gets the comparer.
///
/// The comparer.
public IEqualityComparer Comparer
{
get { return this.comparer.InternalComparer; }
}
// WARNING: The count returned here may include entries for which
// either the key or value objects have already been garbage
// collected. Call RemoveCollectedEntries to weed out collected
// entries and update the count accordingly.
///
/// Gets the count.
///
/// The count.
public override int Count
{
get
{
this.EnsureRemovalOfCollectedEntries();
return this.internalDictionary.Count;
}
}
///
/// Gets the initial capacity.
///
/// The initial capacity.
public int InitialCapacity
{
get { return this.initialCapacity; }
}
///
/// Gets or sets the periodic removal.
///
/// The periodic removal.
public TimeSpan PeriodicRemoval { get; set; }
///
/// Adds the specified key.
///
/// The key.
/// The value.
public override void Add(TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
this.EnsureRemovalOfCollectedEntries();
WeakReference weakKey = new WeakKeyReference(key, this.comparer);
WeakReference weakValue = new WeakReference(value);
this.internalDictionary.Add(weakKey, weakValue);
}
///
/// Clears this instance.
///
public override void Clear()
{
this.internalDictionary.Clear();
}
///
/// Determines whether the specified key contains key.
///
/// The key.
///
/// true if the specified key contains key; otherwise, false .
///
public override bool ContainsKey(TKey key)
{
this.EnsureRemovalOfCollectedEntries();
return this.internalDictionary.ContainsKey(key);
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// An of instance that
/// can be used to iterate through the collection.
///
public override IEnumerator> GetEnumerator()
{
this.EnsureRemovalOfCollectedEntries();
foreach (KeyValuePair> pair in this.internalDictionary)
{
WeakReference weakKey = (WeakReference)pair.Key;
WeakReference weakValue = pair.Value;
TKey key = weakKey.Target;
TValue value = weakValue.Target;
if (weakKey.IsAlive && weakValue.IsAlive)
yield return new KeyValuePair(key, value);
}
}
///
/// Removes the specified key.
///
/// The key.
///
/// true if the was successfully
/// removed; otherwise, false .
///
public override bool Remove(TKey key)
{
var result = this.internalDictionary.Remove(key);
this.EnsureRemovalOfCollectedEntries();
return result;
}
// Removes the left-over weak references for entries in the dictionary
// whose key or value has already been reclaimed by the garbage
// collector. This will reduce the dictionary's Count by the number
// of dead key-value pairs that were eliminated.
///
/// Removes the collected entries.
///
public void RemoveCollectedEntries()
{
List list = null;
foreach (KeyValuePair> pair in this.internalDictionary)
{
WeakReference weakKey = (WeakReference)pair.Key;
WeakReference weakValue = pair.Value;
if (!weakKey.IsAlive || !weakValue.IsAlive)
{
if (list == null)
list = new List();
list.Add(weakKey);
}
}
if (list != null)
{
foreach (object key in list)
this.internalDictionary.Remove(key);
}
}
///
/// Tries the get value.
///
/// The key.
/// The value.
///
/// true if the was successfully
/// found; otherwise, false .
///
public override bool TryGetValue(TKey key, out TValue value)
{
this.EnsureRemovalOfCollectedEntries();
WeakReference weakValue;
if (this.internalDictionary.TryGetValue(key, out weakValue))
{
value = weakValue.Target;
return weakValue.IsAlive;
}
value = default(TValue);
return false;
}
///
/// Sets the value.
///
/// The key.
/// The value.
protected override void SetValue(TKey key, TValue value)
{
WeakReference weakKey = new WeakKeyReference(key, this.comparer);
this.internalDictionary[weakKey] = new WeakReference(value);
}
///
/// Ensures the removal of collected entries.
///
private void EnsureRemovalOfCollectedEntries()
{
if (this.PeriodicRemoval != null && this.lastRemoval.Add(this.PeriodicRemoval) < DateTime.Now)
{
this.RemoveCollectedEntries();
this.lastRemoval = DateTime.Now;
}
}
}
}