/* * * 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; using System.Collections.Generic; namespace Lucene.Net.Support { /// /// A C# emulation of the Java Hashmap /// /// A is a close equivalent to the Java /// Hashmap. One difference java implementation of the class is that /// the Hashmap supports both null keys and values, where the C# Dictionary /// only supports null values not keys. Also, V Get(TKey) /// method in Java returns null if the key doesn't exist, instead of throwing /// an exception. This implementation doesn't throw an exception when a key /// doesn't exist, it will return null. This class is slower than using a /// , because of extra checks that have to be /// done on each access, to check for null. /// /// /// NOTE: This class works best with nullable types. default(T) is returned /// when a key doesn't exist in the collection (this being similar to how Java returns /// null). Therefore, if the expected behavior of the java code is to execute code /// based on if the key exists, when the key is an integer type, it will return 0 instead of null. /// /// /// Consider also implementing IDictionary, IEnumerable, and ICollection /// like does, so HashMap can be /// used in substituted in place for the same interfaces it implements. /// /// /// The type of keys in the dictionary /// The type of values in the dictionary [Serializable] public class HashMap : IDictionary { internal IEqualityComparer _comparer; internal Dictionary _dict; // Indicates if a null key has been assigned, used for iteration private bool _hasNullValue; // stores the value for the null key private TValue _nullValue; // Indicates the type of key is a non-nullable valuetype private bool _isValueType; public HashMap() : this(0) { } public HashMap(IEqualityComparer comparer) : this(0, comparer) { } public HashMap(int initialCapacity) : this(initialCapacity, EqualityComparer.Default) { } public HashMap(int initialCapacity, IEqualityComparer comparer) { _comparer = comparer; _dict = new Dictionary(initialCapacity, _comparer); _hasNullValue = false; if (typeof(TKey).IsValueType) { _isValueType = Nullable.GetUnderlyingType(typeof(TKey)) == null; } } public HashMap(IEnumerable> other) : this(0) { foreach (var kvp in other) { Add(kvp.Key, kvp.Value); } } public bool ContainsValue(TValue value) { if (!_isValueType && _hasNullValue && _nullValue.Equals(value)) return true; return _dict.ContainsValue(value); } #region Implementation of IEnumerable public IEnumerator> GetEnumerator() { if (!_isValueType && _hasNullValue) { yield return new KeyValuePair(default(TKey), _nullValue); } foreach (var kvp in _dict) { yield return kvp; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Implementation of ICollection> void ICollection>.Add(KeyValuePair item) { Add(item.Key, item.Value); } public void Clear() { _hasNullValue = false; _nullValue = default(TValue); _dict.Clear(); } bool ICollection>.Contains(KeyValuePair item) { if (!_isValueType && _comparer.Equals(item.Key, default(TKey))) { return _hasNullValue && EqualityComparer.Default.Equals(item.Value, _nullValue); } return ((ICollection>)_dict).Contains(item); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { ((ICollection>) _dict).CopyTo(array, arrayIndex); if(!_isValueType && _hasNullValue) { array[array.Length - 1] = new KeyValuePair(default(TKey), _nullValue); } } public bool Remove(KeyValuePair item) { if (!_isValueType && _comparer.Equals(item.Key, default(TKey))) { if (!_hasNullValue) return false; _hasNullValue = false; _nullValue = default(TValue); return true; } return ((ICollection>)_dict).Remove(item); } public int Count { get { return _dict.Count + (_hasNullValue ? 1 : 0); } } public bool IsReadOnly { get { return false; } } #endregion #region Implementation of IDictionary public bool ContainsKey(TKey key) { if (!_isValueType && _comparer.Equals(key, default(TKey))) { if (_hasNullValue) { return true; } return false; } return _dict.ContainsKey(key); } public virtual void Add(TKey key, TValue value) { if (!_isValueType && _comparer.Equals(key, default(TKey))) { _hasNullValue = true; _nullValue = value; } else { _dict[key] = value; } } public bool Remove(TKey key) { if (!_isValueType && _comparer.Equals(key, default(TKey))) { _hasNullValue = false; _nullValue = default(TValue); return true; } else { return _dict.Remove(key); } } public bool TryGetValue(TKey key, out TValue value) { if (!_isValueType && _comparer.Equals(key, default(TKey))) { if (_hasNullValue) { value = _nullValue; return true; } value = default(TValue); return false; } else { return _dict.TryGetValue(key, out value); } } public TValue this[TKey key] { get { if (!_isValueType && _comparer.Equals(key, default(TKey))) { if (!_hasNullValue) { return default(TValue); } return _nullValue; } return _dict.ContainsKey(key) ? _dict[key] : default(TValue); } set { Add(key, value); } } public ICollection Keys { get { if (!_hasNullValue) return _dict.Keys; // Using a List to generate an ICollection // would incur a costly copy of the dict's KeyCollection // use out own wrapper instead return new NullKeyCollection(_dict); } } public ICollection Values { get { if (!_hasNullValue) return _dict.Values; // Using a List to generate an ICollection // would incur a costly copy of the dict's ValueCollection // use out own wrapper instead return new NullValueCollection(_dict, _nullValue); } } #endregion #region NullValueCollection /// /// Wraps a dictionary and adds the value /// represented by the null key /// class NullValueCollection : ICollection { private readonly TValue _nullValue; private readonly Dictionary _internalDict; public NullValueCollection(Dictionary dict, TValue nullValue) { _internalDict = dict; _nullValue = nullValue; } #region Implementation of IEnumerable public IEnumerator GetEnumerator() { yield return _nullValue; foreach (var val in _internalDict.Values) { yield return val; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Implementation of ICollection public void CopyTo(TValue[] array, int arrayIndex) { throw new NotImplementedException("Implement as needed"); } public int Count { get { return _internalDict.Count + 1; } } public bool IsReadOnly { get { return true; } } #region Explicit Interface Methods void ICollection.Add(TValue item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(TValue item) { throw new NotSupportedException(); } bool ICollection.Remove(TValue item) { throw new NotSupportedException("Collection is read only!"); } #endregion #endregion } #endregion #region NullKeyCollection /// /// Wraps a dictionary's collection, adding in a /// null key. /// class NullKeyCollection : ICollection { private readonly Dictionary _internalDict; public NullKeyCollection(Dictionary dict) { _internalDict = dict; } public IEnumerator GetEnumerator() { yield return default(TKey); foreach (var key in _internalDict.Keys) { yield return key; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void CopyTo(TKey[] array, int arrayIndex) { throw new NotImplementedException("Implement this as needed"); } public int Count { get { return _internalDict.Count + 1; } } public bool IsReadOnly { get { return true; } } #region Explicit Interface Definitions bool ICollection.Contains(TKey item) { throw new NotSupportedException(); } void ICollection.Add(TKey item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Remove(TKey item) { throw new NotSupportedException(); } #endregion } #endregion } }