/* * 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. */ using System; using System.Collections; namespace Lucene.Net.Support { /// /// A Hashtable which holds weak references to its keys so they /// can be collected during GC. /// [System.Diagnostics.DebuggerDisplay("Count = {Values.Count}")] public class WeakHashTable : Hashtable, IEnumerable { /// /// A weak referene wrapper for the hashtable keys. Whenever a key\value pair /// is added to the hashtable, the key is wrapped using a WeakKey. WeakKey saves the /// value of the original object hashcode for fast comparison. /// class WeakKey { WeakReference reference; int hashCode; public WeakKey(object key) { if (key == null) throw new ArgumentNullException("key"); hashCode = key.GetHashCode(); reference = new WeakReference(key); } public override int GetHashCode() { return hashCode; } public object Target { get { return reference.Target; } } public bool IsAlive { get { return reference.IsAlive; } } } /// /// A Dictionary enumerator which wraps the original hashtable enumerator /// and performs 2 tasks: Extract the real key from a WeakKey and skip keys /// that were already collected. /// class WeakDictionaryEnumerator : IDictionaryEnumerator { IDictionaryEnumerator baseEnumerator; object currentKey; object currentValue; public WeakDictionaryEnumerator(IDictionaryEnumerator baseEnumerator) { this.baseEnumerator = baseEnumerator; } public DictionaryEntry Entry { get { return new DictionaryEntry(this.currentKey, this.currentValue); } } public object Key { get { return this.currentKey; } } public object Value { get { return this.currentValue; } } public object Current { get { return Entry; } } public bool MoveNext() { while (baseEnumerator.MoveNext()) { object key = ((WeakKey)baseEnumerator.Key).Target; if (key != null) { this.currentKey = key; this.currentValue = baseEnumerator.Value; return true; } } return false; } public void Reset() { baseEnumerator.Reset(); this.currentKey = null; this.currentValue = null; } } /// /// Serves as a simple "GC Monitor" that indicates whether cleanup is needed. /// If collectableObject.IsAlive is false, GC has occurred and we should perform cleanup /// WeakReference collectableObject = new WeakReference(new Object()); /// /// Customize the hashtable lookup process by overriding KeyEquals. KeyEquals /// will compare both WeakKey to WeakKey and WeakKey to real keys /// protected override bool KeyEquals(object x, object y) { if (x == y) return true; if (x is WeakKey) { x = ((WeakKey)x).Target; if (x == null) return false; } if (y is WeakKey) { y = ((WeakKey)y).Target; if (y == null) return false; } return x.Equals(y); } protected override int GetHash(object key) { return key.GetHashCode(); } /// /// Perform cleanup if GC occurred /// private void CleanIfNeeded() { if (collectableObject.Target == null) { Clean(); collectableObject = new WeakReference(new Object()); } } /// /// Iterate over all keys and remove keys that were collected /// private void Clean() { foreach (WeakKey wtk in ((Hashtable)base.Clone()).Keys) { if (!wtk.IsAlive) { Remove(wtk); } } } /// /// Wrap each key with a WeakKey and add it to the hashtable /// public override void Add(object key, object value) { CleanIfNeeded(); base.Add(new WeakKey(key), value); } public override IDictionaryEnumerator GetEnumerator() { Hashtable tmp = null; tmp = (Hashtable)base.Clone(); return new WeakDictionaryEnumerator(tmp.GetEnumerator()); } /// /// Create a temporary copy of the real keys and return that /// public override ICollection Keys { get { ArrayList keys = new ArrayList(Count); Hashtable tmpTable = (Hashtable)base.Clone(); foreach (WeakKey key in tmpTable.Keys) { object realKey = key.Target; if (realKey != null) keys.Add(realKey); } return keys; } } public override object this[object key] { get { return base[key]; } set { CleanIfNeeded(); base[new WeakKey(key)] = value; } } public override void CopyTo(Array array, int index) { int arrayIndex = index; foreach (DictionaryEntry de in this) { array.SetValue(de, arrayIndex++); } } public override int Count { get { CleanIfNeeded(); return base.Count; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }