/*
*
* 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
}
}