You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2016/10/02 15:02:35 UTC
[09/10] lucenenet git commit: Fixed several CharArraySet/CharArrayMap
bugs that were causing tests to fail,
and re-enabled those tests. Refactored both classes to be more .NET-like.
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/87d05125/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs b/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
index 37d204f..64a650c 100644
--- a/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
+++ b/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
@@ -3,7 +3,9 @@ using Lucene.Net.Util;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
+using System.Globalization;
using System.Linq;
using System.Text;
@@ -27,45 +29,72 @@ namespace Lucene.Net.Analysis.Util
*/
/// <summary>
- /// A simple class that stores key Strings as char[]'s in a
+ /// A simple class that stores key <see cref="string"/>s as <see cref="char[]"/>'s in a
/// hash table. Note that this is not a general purpose
/// class. For example, it cannot remove items from the
/// map, nor does it resize its hash table to be smaller,
/// etc. It is designed to be quick to retrieve items
- /// by char[] keys without the necessity of converting
- /// to a String first.
+ /// by <see cref="char[]"/> keys without the necessity of converting
+ /// to a <see cref="string"/> first.
///
/// <a name="version"></a>
- /// <para>You must specify the required <seealso cref="Version"/>
- /// compatibility when creating <seealso cref="CharArrayMap"/>:
- /// <ul>
- /// <li> As of 3.1, supplementary characters are
- /// properly lowercased.</li>
- /// </ul>
+ /// <para>You must specify the required <see cref="LuceneVersion"/>
+ /// compatibility when creating <see cref="CharArrayMap"/>:
+ /// <list type="bullet">
+ /// <item> As of 3.1, supplementary characters are
+ /// properly lowercased.</item>
+ /// </list>
/// Before 3.1 supplementary characters could not be
/// lowercased correctly due to the lack of Unicode 4
/// support in JDK 1.4. To use instances of
- /// <seealso cref="CharArrayMap"/> with the behavior before Lucene
- /// 3.1 pass a <seealso cref="Version"/> < 3.1 to the constructors.
+ /// <see cref="CharArrayMap"/> with the behavior before Lucene
+ /// 3.1 pass a <see cref="LuceneVersion"/> < 3.1 to the constructors.
/// </para>
/// </summary>
- public class CharArrayMap<V> : IDictionary<object, V>
+ public class CharArrayMap<TValue> : ICharArrayMap, IDictionary<string, TValue>
{
// private only because missing generics
- private static readonly CharArrayMap<V> EMPTY_MAP = new EmptyCharArrayMap<V>();
+ private static readonly CharArrayMap<TValue> EMPTY_MAP = new CharArrayMap.EmptyCharArrayMap<TValue>();
private const int INIT_SIZE = 8;
private readonly CharacterUtils charUtils;
private readonly bool ignoreCase;
private int count;
- internal readonly LuceneVersion matchVersion; // package private because used in CharArraySet
+ private readonly LuceneVersion matchVersion; // package private because used in CharArraySet
internal char[][] keys; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
- internal V[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
+ internal MapValue[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
/// <summary>
- /// LUCENENET: Added in .NET to prevent infinite recursion when accessing the Keys collection
+ /// LUCENENET: Moved this from CharArraySet so it doesn't need to know the generic type of CharArrayMap
/// </summary>
- private IDictionary<object, V> innerDictionary = new Dictionary<object, V>();
+ internal static readonly MapValue PLACEHOLDER = new MapValue();
+
+ /// <summary>
+ /// LUCENENET SPECIFIC type used to act as a placeholder. Since <c>null</c>
+ /// means that our value is not populated, we need an instance of something
+ /// to indicate it is. Using an instance of <see cref="V"/> would only work if
+ /// we could constrain it with the new() constraint, which isn't possible because
+ /// some types such as <see cref="string"/> don't have a default constructor.
+ /// So, this is a workaround that allows any type regardless of the type of constructor.
+ ///
+ /// <para>
+ /// Note also that we gain the ability to use value types for <see cref="V"/>, but
+ /// also create a difference in behavior from Java Lucene where the actual values
+ /// returned could be <c>null</c>.
+ /// </para>
+ /// </summary>
+ internal class MapValue
+ {
+ public TValue value = default(TValue);
+
+ public MapValue()
+ { }
+
+ public MapValue(TValue value)
+ {
+ this.value = value;
+ }
+ }
/// <summary>
/// Create map with enough capacity to hold startSize terms
@@ -76,8 +105,8 @@ namespace Lucene.Net.Analysis.Util
/// <param name="startSize">
/// the initial capacity </param>
/// <param name="ignoreCase">
- /// <code>false</code> if and only if the set should be case sensitive
- /// otherwise <code>true</code>. </param>
+ /// <c>false</c> if and only if the set should be case sensitive;
+ /// otherwise <c>true</c>. </param>
public CharArrayMap(LuceneVersion matchVersion, int startSize, bool ignoreCase)
{
this.ignoreCase = ignoreCase;
@@ -87,7 +116,7 @@ namespace Lucene.Net.Analysis.Util
size <<= 1;
}
keys = new char[size][];
- values = new V[size];
+ values = new MapValue[size];
this.charUtils = CharacterUtils.GetInstance(matchVersion);
this.matchVersion = matchVersion;
}
@@ -99,11 +128,11 @@ namespace Lucene.Net.Analysis.Util
/// compatibility match version see <a href="#version">Version
/// note</a> above for details. </param>
/// <param name="c">
- /// a map whose mappings to be copied </param>
+ /// a map (<see cref="IDictionary{string, V}"/>) whose mappings to be copied </param>
/// <param name="ignoreCase">
- /// <code>false</code> if and only if the set should be case sensitive
- /// otherwise <code>true</code>. </param>
- public CharArrayMap(LuceneVersion matchVersion, IDictionary<object, V> c, bool ignoreCase)
+ /// <c>false</c> if and only if the set should be case sensitive;
+ /// otherwise <c>true</c>. </param>
+ public CharArrayMap(LuceneVersion matchVersion, IDictionary<string, TValue> c, bool ignoreCase)
: this(matchVersion, c.Count, ignoreCase)
{
foreach (var v in c)
@@ -113,27 +142,9 @@ namespace Lucene.Net.Analysis.Util
}
/// <summary>
- /// LUCENENET specific, in order to support the KeySet functionality.
+ /// Create set from the supplied map (used internally for readonly maps...)
/// </summary>
- /// <param name="keys"></param>
- private CharArrayMap(char[][] keys, V[] values, bool ignoreCase, int count, CharacterUtils charUtils, LuceneVersion matchVersion, Dictionary<object, V> innerDictionary)
- {
- this.keys = keys;
- this.values = values;
- this.ignoreCase = ignoreCase;
- this.count = count;
- this.charUtils = charUtils;
- this.matchVersion = matchVersion;
-
- foreach (var kvp in innerDictionary)
- {
- this.innerDictionary[kvp.Key] = kvp.Value;
- }
- }
-
- /// <summary>
- /// Create set from the supplied map (used internally for readonly maps...) </summary>
- private CharArrayMap(CharArrayMap<V> toCopy)
+ internal CharArrayMap(CharArrayMap<TValue> toCopy)
{
this.keys = toCopy.keys;
this.values = toCopy.values;
@@ -141,59 +152,127 @@ namespace Lucene.Net.Analysis.Util
this.count = toCopy.count;
this.charUtils = toCopy.charUtils;
this.matchVersion = toCopy.matchVersion;
-
- foreach (var kvp in toCopy.innerDictionary)
- {
- this.innerDictionary[kvp.Key] = kvp.Value;
- }
}
- public virtual void Add(KeyValuePair<object, V> item)
+ /// <summary>
+ /// Adds the <see cref="KeyValuePair{string, V}.Value"/> for the passed in <see cref="KeyValuePair{string, V}.Key"/>.
+ /// Note that the <see cref="KeyValuePair{string, V}"/> instance is not added to the dictionary.
+ /// </summary>
+ /// <param name="item">A <see cref="KeyValuePair{string, V}"/> whose <see cref="KeyValuePair{string, V}.Value"/>
+ /// will be added for the corresponding <see cref="KeyValuePair{string, V}.Key"/>. </param>
+ public virtual void Add(KeyValuePair<string, TValue> item)
{
- Put(item.Key, item.Value);
+ Add(item.Key, item.Value);
}
- public virtual void Add(object key, V value)
+ /// <summary>
+ /// Adds the <paramref name="value"/> for the passed in <paramref name="key"/>.
+ /// </summary>
+ /// <param name="key">The string-able type to be added/updated in the dictionary.</param>
+ /// <param name="value">The corresponding value for the given <paramref name="key"/>.</param>
+ public virtual void Add(string key, TValue value)
{
+ if (ContainsKey(key))
+ {
+ throw new ArgumentException("The key " + key + " already exists in the dictionary");
+ }
Put(key, value);
}
/// <summary>
- /// Clears all entries in this map. This method is supported for reusing, but not <seealso cref="Map#remove"/>. </summary>
+ /// Clears all entries in this map. This method is supported for reusing, but not
+ /// <see cref="IDictionary{TKey, TValue}.Remove(TKey)"/>.
+ /// </summary>
public virtual void Clear()
{
count = 0;
Arrays.Fill(keys, null);
- Arrays.Fill(values, default(V));
- innerDictionary.Clear();
+ Arrays.Fill(values, null);
+ }
+
+ /// <summary>
+ /// Not supported.
+ /// </summary>
+ [Obsolete("Not applicable in this class.")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public virtual bool Contains(KeyValuePair<string, TValue> item)
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
+ /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <see cref="arrayIndex"/>.
+ /// The array is assumed to already be dimensioned to fit the elements in this dictionary; otherwise a <see cref="ArgumentOutOfRangeException"/>
+ /// will be thrown.
+ /// </summary>
+ /// <param name="array">The array to copy the items into.</param>
+ /// <param name="arrayIndex">A 32-bit integer that represents the index in <see cref="array"/> at which copying begins.</param>
+ public virtual void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
+ {
+ var iter = (EntryIterator)EntrySet().GetEnumerator();
+ for (int i = arrayIndex; iter.MoveNext(); i++)
+ {
+ array[i] = new KeyValuePair<string, TValue>(iter.Current.Key, iter.CurrentValue);
+ }
+ }
+
+ /// <summary>
+ /// Copies all items in the current <see cref="CharArrayMap{TValue}"/> to the passed in
+ /// <see cref="CharArrayMap{TValue}"/>.
+ /// </summary>
+ /// <param name="map"></param>
+ public virtual void CopyTo(CharArrayMap<TValue> map)
+ {
+ var iter = (EntryIterator)EntrySet().GetEnumerator();
+ while(iter.MoveNext())
+ {
+ map.Put(iter.Current.Key, iter.CurrentValue);
+ }
}
- public virtual bool Contains(KeyValuePair<object, V> item)
+ /// <summary>
+ /// <c>true</c> if the <paramref name="length"/> chars of <paramref name="text"/> starting at <paramref name="offset"/>
+ /// are in the <see cref="KeySet"/>
+ /// </summary>
+ public virtual bool ContainsKey(char[] text, int offset, int length)
{
- throw new NotImplementedException();
+ return keys[GetSlot(text, offset, length)] != null;
}
- public virtual void CopyTo(KeyValuePair<object, V>[] array, int arrayIndex)
+ /// <summary>
+ /// <c>true</c> if the entire <see cref="KeySet"/> is the same as the
+ /// <paramref name="text"/> <see cref="char[]"/> being passed in;
+ /// otherwise <c>false</c>.
+ /// </summary>
+ public virtual bool ContainsKey(char[] text)
{
- throw new NotImplementedException();
+ return keys[GetSlot(text, 0, text.Length)] != null;
}
/// <summary>
- /// true if the <code>len</code> chars of <code>text</code> starting at <code>off</code>
- /// are in the <seealso cref="#keySet()"/>
+ /// <c>true</c> if the <paramref name="text"/> <see cref="string"/> is in the <see cref="KeySet"/>;
+ /// otherwise <c>false</c>
/// </summary>
- public virtual bool ContainsKey(char[] text, int off, int len)
+ public virtual bool ContainsKey(string text)
{
- return keys[GetSlot(text, off, len)] != null;
+ return keys[GetSlot(text)] != null;
}
/// <summary>
- /// true if the <code>CharSequence</code> is in the <seealso cref="#keySet()"/> </summary>
- public virtual bool ContainsKey(string cs)
+ /// <c>true</c> if the <paramref name="text"/> <see cref="ICharSequence"/> is in the <see cref="KeySet"/>;
+ /// otherwise <c>false</c>
+ /// </summary>
+ public virtual bool ContainsKey(ICharSequence cs)
{
return keys[GetSlot(cs)] != null;
}
+
+ /// <summary>
+ /// <c>true</c> if the <paramref name="o"/> <see cref="object.ToString()"/> is in the <see cref="KeySet"/>;
+ /// otherwise <c>false</c>
+ /// </summary>
public virtual bool ContainsKey(object o)
{
if (o == null)
@@ -211,37 +290,85 @@ namespace Lucene.Net.Analysis.Util
}
/// <summary>
- /// returns the value of the mapping of <code>len</code> chars of <code>text</code>
- /// starting at <code>off</code>
+ /// returns the value of the mapping of <paramref name="length"/> chars of <paramref name="text"/>
+ /// starting at <paramref name="offset"/>
+ /// </summary>
+ public virtual TValue Get(char[] text, int offset, int length)
+ {
+ var value = values[GetSlot(text, offset, length)];
+ return (value != null) ? value.value : default(TValue);
+ }
+
+ /// <summary>
+ /// returns the value of the mapping of the chars inside this <paramref name="text"/>
/// </summary>
- public virtual V Get(char[] text, int off, int len)
+ public virtual TValue Get(char[] text)
{
- return values[GetSlot(text, off, len)];
+ var value = values[GetSlot(text, 0, text.Length)];
+ return (value != null) ? value.value : default(TValue);
}
/// <summary>
- /// returns the value of the mapping of the chars inside this {@code CharSequence} </summary>
- public virtual V Get(string cs)
+ /// returns the value of the mapping of the chars inside this <see cref="ICharSequence"/>
+ /// </summary>
+ public virtual TValue Get(ICharSequence cs)
+ {
+ var value = values[GetSlot(cs)];
+ return (value != null) ? value.value : default(TValue);
+ }
+
+ /// <summary>
+ /// returns the value of the mapping of the chars inside this <see cref="string"/>
+ /// </summary>
+ public virtual TValue Get(string cs)
{
- return values[GetSlot(cs)];
+ var value = values[GetSlot(cs)];
+ return (value != null) ? value.value : default(TValue);
}
- public virtual V Get(object o)
+ /// <summary>
+ /// returns the value of the mapping of the chars inside this <see cref="object.ToString()"/>
+ /// </summary>
+ public virtual TValue Get(object o)
{
- var text = o as char[];
- if (text != null)
+ // LUCENENET NOTE: Testing for *is* is at least 10x faster
+ // than casting using *as* and then checking for null.
+ // http://stackoverflow.com/q/1583050/181087
+ if (o is char[])
{
+ var text = o as char[];
return Get(text, 0, text.Length);
}
return Get(o.ToString());
}
- private int GetSlot(char[] text, int off, int len)
+ private int GetSlot(char[] text, int offset, int length)
+ {
+ int code = GetHashCode(text, offset, length);
+ int pos = code & (keys.Length - 1);
+ char[] text2 = keys[pos];
+ if (text2 != null && !Equals(text, offset, length, text2))
+ {
+ int inc = ((code >> 8) + code) | 1;
+ do
+ {
+ code += inc;
+ pos = code & (keys.Length - 1);
+ text2 = keys[pos];
+ } while (text2 != null && !Equals(text, offset, length, text2));
+ }
+ return pos;
+ }
+
+ /// <summary>
+ /// Returns true if the <see cref="ICharSequence"/> is in the set
+ /// </summary>
+ private int GetSlot(ICharSequence text)
{
- int code = GetHashCode(text, off, len);
+ int code = GetHashCode(text);
int pos = code & (keys.Length - 1);
char[] text2 = keys[pos];
- if (text2 != null && !Equals(text, off, len, text2))
+ if (text2 != null && !Equals(text, text2))
{
int inc = ((code >> 8) + code) | 1;
do
@@ -249,13 +376,14 @@ namespace Lucene.Net.Analysis.Util
code += inc;
pos = code & (keys.Length - 1);
text2 = keys[pos];
- } while (text2 != null && !Equals(text, off, len, text2));
+ } while (text2 != null && !Equals(text, text2));
}
return pos;
}
/// <summary>
- /// Returns true if the String is in the set </summary>
+ /// Returns true if the <see cref="string"/> is in the set
+ /// </summary>
private int GetSlot(string text)
{
int code = GetHashCode(text);
@@ -275,27 +403,31 @@ namespace Lucene.Net.Analysis.Util
}
/// <summary>
- /// Add the given mapping. </summary>
- public virtual V Put(ICharSequence text, V value)
+ /// Add the given mapping.
+ /// </summary>
+ public virtual TValue Put(ICharSequence text, TValue value)
{
- return Put(text.ToString(), value); // could be more efficient
+ MapValue oldValue = PutImpl(text, new MapValue(value)); // could be more efficient
+ return (oldValue != null) ? oldValue.value : default(TValue);
}
- public virtual V Put(object o, V value)
+ /// <summary>
+ /// Add the given mapping using the <see cref="object.ToString()"/> representation
+ /// of <paramref name="o"/> in the <see cref="CultureInfo.InvariantCulture"/>.
+ /// </summary>
+ public virtual TValue Put(object o, TValue value)
{
- var c = o as char[];
- if (c != null)
- {
- return Put(c, value);
- }
- return Put(o.ToString(), value);
+ MapValue oldValue = PutImpl(o, new MapValue(value));
+ return (oldValue != null) ? oldValue.value : default(TValue);
}
/// <summary>
- /// Add the given mapping. </summary>
- public virtual V Put(string text, V value)
+ /// Add the given mapping.
+ /// </summary>
+ public virtual TValue Put(string text, TValue value)
{
- return Put(text.ToCharArray(), value);
+ MapValue oldValue = PutImpl(text, new MapValue(value));
+ return (oldValue != null) ? oldValue.value : default(TValue);
}
/// <summary>
@@ -303,7 +435,60 @@ namespace Lucene.Net.Analysis.Util
/// If ignoreCase is true for this Set, the text array will be directly modified.
/// The user should never modify this text array after calling this method.
/// </summary>
- public virtual V Put(char[] text, V value)
+ public virtual TValue Put(char[] text, TValue value)
+ {
+ MapValue oldValue = PutImpl(text, new MapValue(value));
+ return (oldValue != null) ? oldValue.value : default(TValue);
+ }
+
+ /// <summary>
+ /// Add the given mapping.
+ /// </summary>
+ private MapValue PutImpl(ICharSequence text, MapValue value)
+ {
+ return PutImpl(text.ToString(), value); // could be more efficient
+ }
+
+ private MapValue PutImpl(object o, MapValue value)
+ {
+ // LUCENENET NOTE: Testing for *is* is at least 10x faster
+ // than casting using *as* and then checking for null.
+ // http://stackoverflow.com/q/1583050/181087
+ if (o is char[])
+ {
+ var c = o as char[];
+ return PutImpl(c, value);
+ }
+ // LUCENENET: We need value types to be represented using the invariant
+ // culture, so it is consistent regardless of the current culture.
+ // It's easy to work out if this is a value type, but difficult
+ // to get to the ToString(IFormatProvider) overload of the type without
+ // a lot of special cases. It's easier just to change the culture of the
+ // thread before calling ToString(), but we don't want that behavior to
+ // bleed into PutImpl.
+ string s;
+ using (var context = new CultureContext(CultureInfo.InvariantCulture))
+ {
+ s = o.ToString();
+ }
+ return PutImpl(s, value);
+ }
+
+ /// <summary>
+ /// Add the given mapping.
+ /// </summary>
+ private MapValue PutImpl(string text, MapValue value)
+ {
+ return PutImpl(text.ToCharArray(), value);
+ }
+
+ /// <summary>
+ /// LUCENENET specific. Centralizes the logic between <see cref="Put"/>
+ /// implementations that accept a value and those that don't. This value is
+ /// so we know whether or not the value was set, since we can't reliably do
+ /// a check for <c>null</c> on the <see cref="V"/> type.
+ /// </summary>
+ private MapValue PutImpl(char[] text, MapValue value)
{
if (ignoreCase)
{
@@ -312,7 +497,7 @@ namespace Lucene.Net.Analysis.Util
int slot = GetSlot(text, 0, text.Length);
if (keys[slot] != null)
{
- V oldValue = values[slot];
+ MapValue oldValue = values[slot];
values[slot] = value;
return oldValue;
}
@@ -325,22 +510,125 @@ namespace Lucene.Net.Analysis.Util
Rehash();
}
- innerDictionary[text] = value;
+ return null;
+ }
+
+ #region PutAll
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IDictionary{char[],TValue}"/>'s
+ /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+ public virtual void PutAll(IDictionary<char[], TValue> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IDictionary{string,TValue}"/>'s
+ /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+ public virtual void PutAll(IDictionary<string, TValue> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
- return default(V);
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IDictionary{ICharSequence,TValue}"/>'s
+ /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+ public virtual void PutAll(IDictionary<ICharSequence, TValue> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
}
- private void Rehash()
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IDictionary{object,TValue}"/>'s
+ /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+ public virtual void PutAll(IDictionary<object, TValue> collection)
{
- Debug.Assert(keys.Length == values.Length);
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{char[],TValue}}"/>'s
+ /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">The values to add/update in the current map.</param>
+ public virtual void PutAll(IEnumerable<KeyValuePair<char[], TValue>> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{string,TValue}}"/>'s
+ /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">The values to add/update in the current map.</param>
+ public virtual void PutAll(IEnumerable<KeyValuePair<string, TValue>> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{ICharSequence,TValue}}"/>'s
+ /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">The values to add/update in the current map.</param>
+ public virtual void PutAll(IEnumerable<KeyValuePair<ICharSequence, TValue>> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
+
+ /// <summary>
+ /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{object,TValue}}"/>'s
+ /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
+ /// </summary>
+ /// <param name="collection">The values to add/update in the current map.</param>
+ public virtual void PutAll(IEnumerable<KeyValuePair<object, TValue>> collection)
+ {
+ foreach (var kvp in collection)
+ {
+ Put(kvp.Key, kvp.Value);
+ }
+ }
- innerDictionary.Clear();
+ #endregion
+ private void Rehash()
+ {
+ Debug.Assert(keys.Length == values.Length);
int newSize = 2 * keys.Length;
char[][] oldkeys = keys;
- V[] oldvalues = values;
+ MapValue[] oldvalues = values;
keys = new char[newSize][];
- values = new V[newSize];
+ values = new MapValue[newSize];
for (int i = 0; i < oldkeys.Length; i++)
{
@@ -351,24 +639,54 @@ namespace Lucene.Net.Analysis.Util
int slot = GetSlot(text, 0, text.Length);
keys[slot] = text;
values[slot] = oldvalues[i];
+ }
+ }
+ }
- innerDictionary.Add(text, oldvalues[i]);
+ private bool Equals(char[] text1, int offset, int length, char[] text2)
+ {
+ if (length != text2.Length)
+ {
+ return false;
+ }
+ int limit = offset + length;
+ if (ignoreCase)
+ {
+ for (int i = 0; i < length;)
+ {
+ var codePointAt = charUtils.CodePointAt(text1, offset + i, limit);
+ if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
+ {
+ return false;
+ }
+ i += Character.CharCount(codePointAt);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < length; i++)
+ {
+ if (text1[offset + i] != text2[i])
+ {
+ return false;
+ }
}
}
+ return true;
}
- private bool Equals(char[] text1, int off, int len, char[] text2)
+ private bool Equals(ICharSequence text1, char[] text2)
{
- if (len != text2.Length)
+ int length = text1.Length;
+ if (length != text2.Length)
{
return false;
}
- int limit = off + len;
if (ignoreCase)
{
- for (int i = 0; i < len;)
+ for (int i = 0; i < length;)
{
- var codePointAt = charUtils.CodePointAt(text1, off + i, limit);
+ int codePointAt = charUtils.CodePointAt(text1, i);
if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
{
return false;
@@ -378,9 +696,9 @@ namespace Lucene.Net.Analysis.Util
}
else
{
- for (int i = 0; i < len; i++)
+ for (int i = 0; i < length; i++)
{
- if (text1[off + i] != text2[i])
+ if (text1.CharAt(i) != text2[i])
{
return false;
}
@@ -391,14 +709,14 @@ namespace Lucene.Net.Analysis.Util
private bool Equals(string text1, char[] text2)
{
- int len = text1.Length;
- if (len != text2.Length)
+ int length = text1.Length;
+ if (length != text2.Length)
{
return false;
}
if (ignoreCase)
{
- for (int i = 0; i < len;)
+ for (int i = 0; i < length;)
{
int codePointAt = charUtils.CodePointAt(text1, i);
if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
@@ -410,7 +728,7 @@ namespace Lucene.Net.Analysis.Util
}
else
{
- for (int i = 0; i < len; i++)
+ for (int i = 0; i < length; i++)
{
if (text1[i] != text2[i])
{
@@ -421,14 +739,60 @@ namespace Lucene.Net.Analysis.Util
return true;
}
- private int GetHashCode(char[] text, int offset, int len)
+ /// <summary>
+ /// LUCENENET Specific - test for value equality similar to how it is done in Java
+ /// </summary>
+ /// <param name="obj">Another dictionary to test the values of</param>
+ /// <returns><c>true</c> if the given object is an <see cref="IDictionary{object, V}"/> that contains
+ /// the same key value pairs as the current map</returns>
+ public override bool Equals(object obj)
+ {
+ var other = obj as IDictionary<string, TValue>;
+ if (other == null)
+ return false;
+
+ if (this.Count != other.Count)
+ return false;
+
+ var iter = other.GetEnumerator();
+ while (iter.MoveNext())
+ {
+ if (!this.ContainsKey(iter.Current.Key))
+ return false;
+
+ if (!EqualityComparer<TValue>.Default.Equals(this[iter.Current.Key], iter.Current.Value))
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// LUCENENET Specific - override required by .NET because we override Equals
+ /// to simulate Java's value equality checking.
+ /// </summary>
+ /// <returns></returns>
+ public override int GetHashCode()
+ {
+ const int PRIME = 31; // arbitrary prime
+ int hash = PRIME;
+ var iter = (EntryIterator)EntrySet().GetEnumerator();
+ while (iter.MoveNext())
+ {
+ hash = (hash * PRIME) ^ iter.Current.Key.GetHashCode();
+ hash = (hash * PRIME) ^ iter.Current.Value.GetHashCode();
+ }
+ return hash;
+ }
+
+ private int GetHashCode(char[] text, int offset, int length)
{
if (text == null)
{
throw new ArgumentException("text can't be null", "text");
}
int code = 0;
- int stop = offset + len;
+ int stop = offset + length;
if (ignoreCase)
{
for (int i = offset; i < stop;)
@@ -448,6 +812,34 @@ namespace Lucene.Net.Analysis.Util
return code;
}
+ private int GetHashCode(ICharSequence text)
+ {
+ if (text == null)
+ {
+ throw new ArgumentException("text can't be null", "text");
+ }
+
+ int code = 0;
+ int length = text.Length;
+ if (ignoreCase)
+ {
+ for (int i = 0; i < length;)
+ {
+ int codePointAt = charUtils.CodePointAt(text, i);
+ code = code * 31 + Character.ToLowerCase(codePointAt);
+ i += Character.CharCount(codePointAt);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < length; i++)
+ {
+ code = code * 31 + text.CharAt(i);
+ }
+ }
+ return code;
+ }
+
private int GetHashCode(string text)
{
if (text == null)
@@ -456,10 +848,10 @@ namespace Lucene.Net.Analysis.Util
}
int code = 0;
- int len = text.Length;
+ int length = text.Length;
if (ignoreCase)
{
- for (int i = 0; i < len;)
+ for (int i = 0; i < length;)
{
int codePointAt = charUtils.CodePointAt(text, i);
code = code * 31 + Character.ToLowerCase(codePointAt);
@@ -468,7 +860,7 @@ namespace Lucene.Net.Analysis.Util
}
else
{
- for (int i = 0; i < len; i++)
+ for (int i = 0; i < length; i++)
{
code = code * 31 + text[i];
}
@@ -478,664 +870,2075 @@ namespace Lucene.Net.Analysis.Util
#region For .NET Support LUCENENET
- public virtual bool TryGetValue(object key, out V value)
+ /// <summary>
+ /// The Lucene version corresponding to the compatibility behavior
+ /// that this instance emulates
+ /// </summary>
+ public virtual LuceneVersion MatchVersion
{
- throw new NotImplementedException();
+ get { return matchVersion; }
}
- public virtual V this[object key]
- {
- get { return Get(key); }
- set { Put(key, value); }
+ /// <summary>
+ /// Adds a placeholder with the given <paramref name="text"/> as the key.
+ /// Primarily for internal use by <see cref="CharArraySet"/>.
+ /// </summary>
+ public virtual bool Put(char[] text)
+ {
+ return PutImpl(text, PLACEHOLDER) == null;
}
- public virtual ICollection<object> Keys { get { return KeySet(); } }
-
- public ICollection<V> Values { get { return values; } }
-
- #endregion
-
- public virtual bool IsReadOnly { get; private set; }
-
- public virtual IEnumerator<KeyValuePair<object, V>> GetEnumerator()
+ /// <summary>
+ /// Adds a placeholder with the given <paramref name="text"/> as the key.
+ /// Primarily for internal use by <see cref="CharArraySet"/>.
+ /// </summary>
+ public virtual bool Put(ICharSequence text)
{
- return new EntryIterator(this, false);
+ return PutImpl(text, PLACEHOLDER) == null;
}
- IEnumerator IEnumerable.GetEnumerator()
+ /// <summary>
+ /// Adds a placeholder with the given <paramref name="text"/> as the key.
+ /// Primarily for internal use by <see cref="CharArraySet"/>.
+ /// </summary>
+ public virtual bool Put(string text)
{
- return GetEnumerator();
+ return PutImpl(text, PLACEHOLDER) == null;
}
-
- public virtual bool Remove(object key)
+ /// <summary>
+ /// Adds a placeholder with the given <paramref name="o"/> as the key.
+ /// Primarily for internal use by <see cref="CharArraySet"/>.
+ /// </summary>
+ public virtual bool Put(object o)
{
- throw new NotSupportedException();
+ return PutImpl(o, PLACEHOLDER) == null;
}
- public virtual bool Remove(KeyValuePair<object, V> item)
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
+ /// <param name="length">The total length of the <paramref name="key"/>.</param>
+ /// <param name="value">When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.</param>
+ /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+ public virtual bool TryGetValue(char[] key, int offset, int length, out TValue value)
{
- throw new NotSupportedException();
+ var val = values[GetSlot(key, offset, length)];
+ if (val != null)
+ {
+ value = val.value;
+ return true;
+ }
+ value = default(TValue);
+ return false;
}
- public virtual int Count
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.</param>
+ /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+ public virtual bool TryGetValue(char[] key, out TValue value)
{
- get { return count; }
+ var val = values[GetSlot(key, 0, key.Length)];
+ if (val != null)
+ {
+ value = val.value;
+ return true;
+ }
+ value = default(TValue);
+ return false;
}
- public override string ToString()
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.</param>
+ /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+ public virtual bool TryGetValue(ICharSequence key, out TValue value)
{
- var sb = new StringBuilder("{");
-
- IEnumerator<KeyValuePair<object, V>> iter1 = IDictionaryExtensions.EntrySet(this).GetEnumerator();
- while (iter1.MoveNext())
+ var val = values[GetSlot(key)];
+ if (val != null)
{
- KeyValuePair<object, V> entry = iter1.Current;
- if (sb.Length > 1)
- {
- sb.Append(", ");
- }
- if (entry.Key.GetType().Equals(typeof(char[])))
- {
- sb.Append(new string((char[])entry.Key));
- }
- else
- {
- sb.Append(entry.Key);
- }
- sb.Append("=");
- sb.Append(entry.Value);
+ value = val.value;
+ return true;
}
-
- return sb.Append('}').ToString();
+ value = default(TValue);
+ return false;
}
- private EntrySet_ entrySet = null;
- private CharArraySet keySet = null;
-
- internal virtual EntrySet_ CreateEntrySet()
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.</param>
+ /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+ public virtual bool TryGetValue(string key, out TValue value)
{
- return new EntrySet_(this, true);
+ var val = values[GetSlot(key)];
+ if (val != null)
+ {
+ value = val.value;
+ return true;
+ }
+ value = default(TValue);
+ return false;
}
- public EntrySet_ EntrySet()
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">When this method returns, contains the value associated with the specified key,
+ /// if the key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.</param>
+ /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+ public virtual bool TryGetValue(object key, out TValue value)
{
- if (entrySet == null)
+ // LUCENENET NOTE: Testing for *is* is at least 10x faster
+ // than casting using *as* and then checking for null.
+ // http://stackoverflow.com/q/1583050/181087
+ if (key is char[])
{
- entrySet = CreateEntrySet();
+ var text = key as char[];
+ return TryGetValue(text, 0, text.Length, out value);
}
- return entrySet;
+ return TryGetValue(key.ToString(), out value);
}
- // helper for CharArraySet to not produce endless recursion
- internal IEnumerable<object> OriginalKeySet()
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
+ /// <param name="length">The total length of the <paramref name="key"/>.</param>
+ public virtual TValue this[char[] key, int offset, int length]
{
- return innerDictionary.Keys;
+ get { return Get(key, offset, length); }
+ set { Put(key, value); }
}
/// <summary>
- /// Returns an <seealso cref="CharArraySet"/> view on the map's keys.
- /// The set will use the same {@code matchVersion} as this map.
+ /// Gets or sets the value associated with the specified key.
/// </summary>
- private CharArraySet KeySet()
+ /// <param name="key">The key of the value to get or set.</param>
+ public virtual TValue this[char[] key]
{
- if (keySet == null)
- {
- // prevent adding of entries
+ get { return Get(key); }
+ set { Put(key, value); }
+ }
- // LUCENENET TODO: Should find a way to pass a direct reference to this object to
- // CharArraySet's constructor. The only known side effect this causes is to make
- // the ToString test fail when calling it on the Keys property after changing
- // the main set, so perhaps this is not a huge issue.
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ public virtual TValue this[ICharSequence key]
+ {
+ get { return Get(key); }
+ set { Put(key, value); }
+ }
- // Then again, any derived classes of this one could have serious issues without
- // the direct reference.
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ public virtual TValue this[string key]
+ {
+ get { return Get(key); }
+ set { Put(key, value); }
+ }
- // A possible solution is to create an interface with no generics that this class
- // implements to be passed into the CharArraySet's constructor. Another possible
- // solution is to make the CharArraySet class generic so the inner type can be
- // inferred at compile time.
- var innerDictionaryObj = new Dictionary<object, object>();
- foreach (var kvp in this.innerDictionary)
- {
- innerDictionaryObj.Add(kvp.Key, kvp.Value);
- }
- var copy = new CharArrayMap<object>(this.keys, this.values.Cast<object>().ToArray(),
- this.ignoreCase, this.count, this.charUtils, this.matchVersion, innerDictionaryObj);
- keySet = new UnmodifiableCharArraySet(copy);
- }
- return keySet;
+ /// <summary>
+ /// Gets or sets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ public virtual TValue this[object key]
+ {
+ get { return Get(key); }
+ set { Put(key, value); }
}
- private sealed class UnmodifiableCharArraySet : CharArraySet
+ /// <summary>
+ /// Gets a collection containing the keys in the <see cref="CharArrayMap{TValue}"/>.
+ /// </summary>
+ public virtual ICollection<string> Keys
{
- internal UnmodifiableCharArraySet(CharArrayMap<object> map) : base(map)
- {
- }
+ get { return KeySet; }
+ }
- public override bool Add(object o)
- {
- throw new NotSupportedException();
- }
- public bool Add(ICharSequence text)
- {
- throw new NotSupportedException();
- }
- public override bool Add(string text)
- {
- throw new NotSupportedException();
- }
- public override bool Add(char[] text)
- {
- throw new NotSupportedException();
- }
- public override void Clear()
- {
- throw new NotSupportedException();
- }
- public override bool Remove(object item)
+
+ private volatile ICollection<TValue> valueSet;
+
+ /// <summary>
+ /// Gets a collection containing the values in the <see cref="CharArrayMap{TValue}"/>.
+ /// This specialized collection can be enumerated in order to read its values and
+ /// overrides <see cref="object.ToString()"/> in order to display a string
+ /// representation of the values.
+ /// </summary>
+ public ICollection<TValue> Values
+ {
+ get
{
- throw new NotSupportedException();
+ if (valueSet == null)
+ {
+ valueSet = new ValueCollection(this);
+ }
+ return valueSet;
}
}
/// <summary>
- /// public iterator class so efficient methods are exposed to users
+ /// LUCENENET specific class used to break the infinite recursion when the
+ /// CharArraySet iterates the keys of this dictionary via <see cref="OriginalKeySet"/>.
+ /// In Java, the keyset of the abstract base class was used to break the infinite recursion,
+ /// however this class doesn't have an abstract base class so that is not an option.
+ /// This class is just a facade around the keys (not another collection of keys), so it
+ /// doesn't consume any additional RAM while providing another "virtual" collection to iterate over.
/// </summary>
- public class EntryIterator : IEnumerator<KeyValuePair<object, V>>
+ internal class KeyCollection : ICollection<string>
{
- private readonly CharArrayMap<V> outerInstance;
-
- internal int pos = -1;
- internal int lastPos;
- internal readonly bool allowModify;
+ private readonly CharArrayMap<TValue> outerInstance;
- internal EntryIterator(CharArrayMap<V> outerInstance, bool allowModify)
+ public KeyCollection(CharArrayMap<TValue> outerInstance)
{
this.outerInstance = outerInstance;
- this.allowModify = allowModify;
- GoNext();
}
- internal void GoNext()
+ public int Count
{
- lastPos = pos;
- pos++;
- while (pos < outerInstance.keys.Length && outerInstance.keys[pos] == null)
+ get
{
- pos++;
+ return outerInstance.Count;
}
}
- public virtual bool HasNext()
+ public bool IsReadOnly
{
- return pos < outerInstance.keys.Length;
+ get
+ {
+ return outerInstance.IsReadOnly;
+ }
}
- /// <summary>
- /// gets the next key... do not modify the returned char[] </summary>
- public virtual char[] NextKey()
+ public void Add(string item)
{
- GoNext();
- return outerInstance.keys[lastPos];
+ throw new NotSupportedException();
}
- /// <summary>
- /// gets the next key as a newly created String object </summary>
- public virtual string NextKeyString()
+ public void Clear()
{
- return new string(NextKey());
+ outerInstance.Clear();
}
- /// <summary>
- /// returns the value associated with the last key returned </summary>
- public virtual V CurrentValue()
+ public bool Contains(string item)
{
- return outerInstance.values[lastPos];
+ return outerInstance.ContainsKey(item);
}
- /// <summary>
- /// sets the value associated with the last key returned </summary>
- public virtual V SetValue(V value)
+ public void CopyTo(string[] array, int arrayIndex)
{
- if (!allowModify)
+ var iter = GetEnumerator();
+ for (int i = arrayIndex; iter.MoveNext(); i++)
{
- throw new System.NotSupportedException();
+ array[i] = iter.Current;
}
- V old = outerInstance.values[lastPos];
- outerInstance.values[lastPos] = value;
- return old;
}
- /// <summary>
- /// use nextCharArray() + currentValue() for better efficiency.
- /// </summary>
- //public virtual KeyValuePair<object, V> Next()
- //{
- // GoNext();
- // //return new KeyValuePair<object, V>();
- // return new MapEntry(outerInstance, lastPos, allowModify);
- //}
-
- public virtual void Remove()
+ public IEnumerator<string> GetEnumerator()
{
- throw new NotSupportedException();
+ return new KeyEnumerator(outerInstance);
}
- #region Added for better .NET support LUCENENET
- public virtual void Dispose()
+ public bool Remove(string item)
{
+ throw new NotSupportedException();
}
- public virtual bool MoveNext()
+ IEnumerator IEnumerable.GetEnumerator()
{
- if (!HasNext()) return false;
- GoNext();
- return true;
+ return GetEnumerator();
}
- public virtual void Reset()
+ /// <summary>
+ /// LUCENENET specific class to iterate the values in the <see cref="KeyCollection"/>.
+ /// </summary>
+ private class KeyEnumerator : IEnumerator<string>
{
- pos = -1;
- GoNext();
- }
+ private readonly CharArrayMap<TValue> outerInstance;
+ private readonly EntryIterator entryIterator;
- public virtual KeyValuePair<object, V> Current { get { return new KeyValuePair<object, V>(outerInstance.keys[lastPos], outerInstance.values[lastPos]); } private set { } }
+ public KeyEnumerator(CharArrayMap<TValue> outerInstance)
+ {
+ this.outerInstance = outerInstance;
+ this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
+ }
- object IEnumerator.Current
- {
- get { return CurrentValue(); }
- }
+ public string Current
+ {
+ get
+ {
+ return entryIterator.Current.Key;
+ }
+ }
- #endregion
- }
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
- // LUCENENET NOTE: The Java Lucene type MapEntry was removed here because it is not possible
- // to inherit the value type KeyValuePair in .NET.
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool MoveNext()
+ {
+ return entryIterator.MoveNext();
+ }
+
+ public void Reset()
+ {
+ entryIterator.Reset();
+ }
+ }
+ }
/// <summary>
- /// public EntrySet_ class so efficient methods are exposed to users
- ///
- /// NOTE: In .NET this was renamed to EntrySet_ because it conflicted with the
- /// method EntrySet(). Since there is also an extension method named IDictionary<K,V>.EntrySet()
- /// that this class needs to override, changing the name of the method was not
- /// possible because the extension method would produce incorrect results if it were
- /// inadvertently called.
+ /// LUCENENET specific class that represents the values in the <see cref="CharArrayMap{TValue}"/>.
/// </summary>
- public sealed class EntrySet_ : ISet<KeyValuePair<object, V>>
+ internal class ValueCollection : ICollection<TValue>
{
- private readonly CharArrayMap<V> outerInstance;
-
- internal readonly bool allowModify;
+ private readonly CharArrayMap<TValue> outerInstance;
- internal EntrySet_(CharArrayMap<V> outerInstance, bool allowModify)
+ public ValueCollection(CharArrayMap<TValue> outerInstance)
{
this.outerInstance = outerInstance;
- this.allowModify = allowModify;
- }
-
- public IEnumerator GetEnumerator()
- {
- return new EntryIterator(outerInstance, allowModify);
}
- IEnumerator<KeyValuePair<object, V>> IEnumerable<KeyValuePair<object, V>>.GetEnumerator()
+ public int Count
{
- return (IEnumerator<KeyValuePair<object, V>>)GetEnumerator();
+ get
+ {
+ return outerInstance.Count;
+ }
}
- public bool Contains(object o)
+ public bool IsReadOnly
{
- if (!(o is DictionaryEntry))
+ get
{
- return false;
+ return outerInstance.IsReadOnly;
}
- var e = (KeyValuePair<object, V>)o;
- object key = e.Key;
- object val = e.Value;
- object v = outerInstance.Get(key);
- return v == null ? val == null : v.Equals(val);
}
- public bool Remove(KeyValuePair<object, V> item)
+ public void Add(TValue item)
{
throw new NotSupportedException();
}
- public int Count { get { return outerInstance.count; } private set { throw new NotSupportedException(); } }
-
public void Clear()
{
- if (!allowModify)
- {
- throw new NotSupportedException();
- }
outerInstance.Clear();
}
- #region Added for better .NET support LUCENENET
-
- #region Not implemented members
-
- // TODO: Either implement these (and write tests for them) or throw
- // NotSupportedException.
- public void CopyTo(KeyValuePair<object, V>[] array, int arrayIndex)
- {
- throw new NotImplementedException();
- }
-
- public bool Contains(KeyValuePair<object, V> item)
+ public bool Contains(TValue item)
{
- throw new NotImplementedException();
+ return outerInstance.values.Select(x => (x != null) ? x.value : default(TValue)).Contains(item);
}
- public void Add(KeyValuePair<object, V> item)
+ public void CopyTo(TValue[] array, int arrayIndex)
{
- throw new NotImplementedException();
+ throw new NotSupportedException();
}
- public void UnionWith(IEnumerable<KeyValuePair<object, V>> other)
+ public IEnumerator<TValue> GetEnumerator()
{
- throw new NotImplementedException();
+ return new ValueEnumerator(outerInstance);
}
- public void IntersectWith(IEnumerable<KeyValuePair<object, V>> other)
+ public bool Remove(TValue item)
{
- throw new NotImplementedException();
+ throw new NotSupportedException();
}
- public void ExceptWith(IEnumerable<KeyValuePair<object, V>> other)
+ IEnumerator IEnumerable.GetEnumerator()
{
- throw new NotImplementedException();
+ return GetEnumerator();
}
- public void SymmetricExceptWith(IEnumerable<KeyValuePair<object, V>> other)
+ public override string ToString()
{
- throw new NotImplementedException();
- }
+ var i = (ValueEnumerator)GetEnumerator();
+ if (!i.HasNext)
+ return "[]";
- public bool IsSubsetOf(IEnumerable<KeyValuePair<object, V>> other)
- {
- throw new NotImplementedException();
- }
+ StringBuilder sb = new StringBuilder();
+ sb.Append('[');
+ while(i.MoveNext())
+ {
+ TValue value = i.Current;
+ if (sb.Length > 1)
+ {
+ sb.Append(',').Append(' ');
+ }
+ sb.Append(value.ToString());
+ }
- public bool IsSupersetOf(IEnumerable<KeyValuePair<object, V>> other)
- {
- throw new NotImplementedException();
+ return sb.Append(']').ToString();
}
- public bool IsProperSupersetOf(IEnumerable<KeyValuePair<object, V>> other)
+ /// <summary>
+ /// LUCENENET specific class to enumerate the values in the <see cref="ValueCollection"/>.
+ /// </summary>
+ private class ValueEnumerator : IEnumerator<TValue>
{
- throw new NotImplementedException();
- }
+ private readonly CharArrayMap<TValue> outerInstance;
+ private readonly EntryIterator entryIterator;
- public bool IsProperSubsetOf(IEnumerable<KeyValuePair<object, V>> other)
- {
- throw new NotImplementedException();
- }
+ public ValueEnumerator(CharArrayMap<TValue> outerInstance)
+ {
+ this.outerInstance = outerInstance;
+ this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
+ }
- public bool Overlaps(IEnumerable<KeyValuePair<object, V>> other)
- {
- throw new NotImplementedException();
- }
+ public TValue Current
+ {
+ get
+ {
+ return entryIterator.CurrentValue;
+ }
+ }
- public bool SetEquals(IEnumerable<KeyValuePair<object, V>> other)
- {
- throw new NotImplementedException();
- }
-
- bool ISet<KeyValuePair<object, V>>.Add(KeyValuePair<object, V> item)
- {
- throw new NotImplementedException();
- }
- #endregion
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
- public bool IsReadOnly { get { return !allowModify; } }
+ public void Dispose()
+ {
+ // nothing to do
+ }
- public override string ToString()
- {
- var sb = new StringBuilder("[");
+ public bool MoveNext()
+ {
+ return entryIterator.MoveNext();
+ }
- IEnumerator<KeyValuePair<object, V>> iter1 = new EntryIterator(this.outerInstance, false);
- while (iter1.MoveNext())
+ public void Reset()
{
- KeyValuePair<object, V> entry = iter1.Current;
- if (sb.Length > 1)
- {
- sb.Append(", ");
- }
- if (entry.Key.GetType().Equals(typeof(char[])))
- {
- sb.Append(new string((char[])entry.Key));
- }
- else
- {
- sb.Append(entry.Key);
- }
- sb.Append("=");
- sb.Append(entry.Value);
+ entryIterator.Reset();
}
- return sb.Append(']').ToString();
+ public bool HasNext
+ {
+ get { return entryIterator.HasNext; }
+ }
}
- #endregion
}
+ #endregion
+
/// <summary>
- /// Returns an unmodifiable <seealso cref="CharArrayMap"/>. This allows to provide
- /// unmodifiable views of internal map for "read-only" use.
+ /// <c>true</c> if the <see cref="CharArrayMap{TValue}"/> is read-only; otherwise <c>false</c>.
/// </summary>
- /// <param name="map">
- /// a map for which the unmodifiable map is returned. </param>
- /// <returns> an new unmodifiable <seealso cref="CharArrayMap"/>. </returns>
- /// <exception cref="NullPointerException">
- /// if the given map is <code>null</code>. </exception>
- public static CharArrayMap<V> UnmodifiableMap(CharArrayMap<V> map)
+ public virtual bool IsReadOnly { get; private set; }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
+ /// </summary>
+ public virtual IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
{
- if (map == null)
- {
- throw new ArgumentException("Given map is null", "map");
- }
- if (map == EmptyMap() || map.Count == 0)
+ return new EntryIterator(this, false);
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
+ /// </summary>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ [Obsolete("Not applicable in this class.")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public virtual bool Remove(string key)
+ {
+ throw new NotSupportedException();
+ }
+
+ [Obsolete("Not applicable in this class.")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public virtual bool Remove(KeyValuePair<string, TValue> item)
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
+ /// Gets the number of key/value pairs contained in the <see cref="CharArrayMap{TValue}"/>.
+ /// </summary>
+ public virtual int Count
+ {
+ get { return count; }
+ }
+
+ /// <summary>
+ /// Returns a string that represents the current object. (Inherited from <see cref="object"/>.)
+ /// </summary>
+ public override string ToString()
+ {
+ var sb = new StringBuilder("{");
+
+ IEnumerator<KeyValuePair<string, TValue>> iter1 = IDictionaryExtensions.EntrySet(this).GetEnumerator();
+ while (iter1.MoveNext())
{
- return EmptyMap();
+ KeyValuePair<string, TValue> entry = iter1.Current;
+ if (sb.Length > 1)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(entry.Key);
+ sb.Append("=");
+ sb.Append(entry.Value);
}
- if (map is UnmodifiableCharArrayMap<V>)
+
+ return sb.Append('}').ToString();
+ }
+
+ private EntrySet_ entrySet = null;
+ private CharArraySet keySet = null;
+ private KeyCollection originalKeySet = null;
+
+ internal virtual EntrySet_ CreateEntrySet()
+ {
+ return new EntrySet_(this, true);
+ }
+
+ // LUCENENET NOTE: This MUST be a method, since there is an
+ // extension method that this class needs to override the behavior of.
+ public EntrySet_ EntrySet()
+ {
+ if (entrySet == null)
{
- return map;
+ entrySet = CreateEntrySet();
}
- return new UnmodifiableCharArrayMap<V>(map);
+ return entrySet;
}
/// <summary>
- /// Returns a copy of the given map as a <seealso cref="CharArrayMap"/>. If the given map
- /// is a <seealso cref="CharArrayMap"/> the ignoreCase property will be preserved.
- /// <para>
- /// <b>Note:</b> If you intend to create a copy of another <seealso cref="CharArrayMap"/> where
- /// the <seealso cref="Version"/> of the source map differs from its copy
- /// <seealso cref="#CharArrayMap(Version, Map, boolean)"/> should be used instead.
- /// The <seealso cref="#copy(Version, Map)"/> will preserve the <seealso cref="Version"/> of the
- /// source map it is an instance of <seealso cref="CharArrayMap"/>.
- /// </para>
+ /// helper for CharArraySet to not produce endless recursion
/// </summary>
- /// <param name="matchVersion">
- /// compatibility match version see <a href="#version">Version
- /// note</a> above for details. This argument will be ignored if the
- /// given map is a <seealso cref="CharArrayMap"/>. </param>
- /// <param name="map">
- /// a map to copy </param>
- /// <returns> a copy of the given map as a <seealso cref="CharArrayMap"/>. If the given map
- /// is a <seealso cref="CharArrayMap"/> the ignoreCase property as well as the
- /// matchVersion will be of the given map will be preserved. </returns>
- public static CharArrayMap<V> Copy(LuceneVersion matchVersion, IDictionary<object, V> map)
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public ICollection<string> OriginalKeySet
{
- if (map == EMPTY_MAP)
+ get
{
- return EmptyMap();
+ if (originalKeySet == null)
+ {
+ // prevent adding of entries
+ originalKeySet = new KeyCollection(this);
+ }
+ return originalKeySet;
}
+ }
- var vs = map as CharArrayMap<V>;
- if (vs != null)
+ /// <summary>
+ /// Returns an <see cref="CharArraySet"/> view on the map's keys.
+ /// The set will use the same <see cref="matchVersion"/> as this map.
+ /// </summary>
+ private CharArraySet KeySet
+ {
+ get
{
- var m = vs;
- // use fast path instead of iterating all values
- // this is even on very small sets ~10 times faster than iterating
- var keys = new char[m.keys.Length][];
- Array.Copy(m.keys, 0, keys, 0, keys.Length);
- var values = new V[m.values.Length];
- Array.Copy(m.values, 0, values, 0, values.Length);
-
- IDictionary<object, V> innerDictionaryCopy = new Dictionary<object, V>();
- for (int i = 0; i < keys.Length; i++)
+ if (keySet == null)
{
- if (keys[i] != null)
- innerDictionaryCopy.Add(keys[i], values[i]);
+ // prevent adding of entries
+ keySet = new UnmodifiableCharArraySet(this);
}
- m = new CharArrayMap<V>(m) { keys = keys, values = values, innerDictionary = innerDictionaryCopy };
- return m;
+ return keySet;
}
- return new CharArrayMap<V>(matchVersion, map, false);
}
- /// <summary>
- /// Returns an empty, unmodifiable map. </summary>
- public static CharArrayMap<V> EmptyMap()
+ private sealed class UnmodifiableCharArraySet : CharArraySet
{
- return EMPTY_MAP;
+ internal UnmodifiableCharArraySet(ICharArrayMap map)
+ : base(map)
+ {
+ }
+
+ public override bool Add(object o)
+ {
+ throw new NotSupportedException();
+ }
+ public override bool Add(ICharSequence text)
+ {
+ throw new NotSupportedException();
+ }
+ public override bool Add(string text)
+ {
+ throw new NotSupportedException();
+ }
+ public override bool Add(char[] text)
+ {
+ throw new NotSupportedException();
+ }
}
- // package private CharArraySet instanceof check in CharArraySet
- internal class UnmodifiableCharArrayMap<V> : CharArrayMap<V>
+ /// <summary>
+ /// public iterator class so efficient methods are exposed to users
+ /// </summary>
+ public class EntryIterator : IEnumerator<KeyValuePair<string, TValue>>
{
+ private readonly CharArrayMap<TValue> outerInstance;
- public UnmodifiableCharArrayMap(CharArrayMap<V> map) : base(map)
- {
+ internal int pos = -1;
+ internal int lastPos;
+ internal readonly bool allowModify;
- }
-
- public override void Clear()
+ internal EntryIterator(CharArrayMap<TValue> outerInstance, bool allowModify)
{
- throw new System.NotSupportedException();
+ this.outerInstance = outerInstance;
+ this.allowModify = allowModify;
+ GoNext();
}
- public override V Put(object o, V val)
+ internal void GoNext()
{
- throw new System.NotSupportedException();
+ lastPos = pos;
+ pos++;
+ while (pos < outerInstance.keys.Length && outerInstance.keys[pos] == null)
+ {
+ pos++;
+ }
}
- public override V Put(char[] text, V val)
+ public virtual bool HasNext
{
- throw new System.NotSupportedException();
+ get { return pos < outerInstance.keys.Length; }
}
- public override V Put(ICharSequence text, V val)
+ /// <summary>
+ /// gets the next key... do not modify the returned char[]
+ /// </summary>
+ public virtual char[] NextKey()
{
- throw new System.NotSupportedException();
+ GoNext();
+ return outerInstance.keys[lastPos];
}
- public override V Put(string text, V val)
+ /// <summary>
+ /// gets the next key as a newly created <see cref="string"/> object
+ /// </summary>
+ public virtual string NextKeyString()
{
- throw new System.NotSupportedException();
+ return new string(NextKey());
}
- public override bool Remove(object key)
+ /// <summary>
+ /// returns the value associated with the current key
+ /// </summary>
+ public virtual TValue CurrentValue
{
- throw new System.NotSupportedException();
+ get
+ {
+ var val = outerInstance.values[lastPos];
+ return val != null ? val.value : default(TValue);
+ }
}
- internal override EntrySet_ CreateEntrySet()
+ /// <summary>
+ /// sets the value associated with the last key returned
+ /// </summary>
+ public virtual TValue SetValue(TValue value)
{
- return new EntrySet_(this, false);
+ if (!allowModify)
+ {
+ throw new NotSupportedException();
+ }
+ TValue old = outerInstance.values[lastPos].value;
+ outerInstance.values[lastPos].value = value;
+ return old;
}
+ // LUCENENET: Next() and Remove() methods eliminated here
+
#region Added for better .NET support LUCENENET
- public override void Add(object key, V value)
+ public virtual void Dispose()
+ {
+ // nothing to do
+ }
+
+ public virtual bool MoveNext()
{
- throw new System.NotSupportedException();
+ if (!HasNext) return false;
+ GoNext();
+ return true;
}
- public override void Add(KeyValuePair<object, V> item)
+
+ public virtual void Reset()
{
- throw new System.NotSupportedException();
+ pos = -1;
+ GoNext();
}
- public override V this[object key]
+
+ public virtual KeyValuePair<string, TValue> Current
{
- get { return base[key]; }
- set { throw new System.NotSupportedException(); }
+ get
+ {
+ var val = outerInstance.values[lastPos];
+ return new KeyValuePair<string, TValue>(
+ new string(outerInstance.keys[lastPos]),
+ val != null ? val.value : default(TValue));
+ }
}
- public override bool Remove(KeyValuePair<object, V> item)
+
+ object IEnumerator.Current
{
- throw new System.NotSupportedException();
+ get { return Current; }
}
+
#endregion
}
+ // LUCENENET NOTE: The Java Lucene type MapEntry was removed here because it is not possible
+ // to inherit the value type KeyValuePair{TKey, TValue} in .NET.
+
/// <summary>
- /// Empty <seealso cref="CharArrayMap{V}.UnmodifiableCharArrayMap"/> optimized for speed.
- /// Contains checks will always return <code>false</code> or throw
- /// NPE if necessary.
+ /// public EntrySet_ class so efficient methods are exposed to users
+ ///
+ /// NOTE: In .NET this was renamed to EntrySet_ because it conflicted with the
+ /// method EntrySet(). Since there is also an extension method named <see cref="IDictionary{K,V}.EntrySet()"/>
+ /// that this class needs to override, changing the name of the method was not
+ /// possible because the extension method would produce incorrect results if it were
+ /// inadvertently called, leading to hard-to-diagnose bugs.
+ ///
+ /// Another difference between this set and the Java counterpart is that it implements
+ /// <see cref="ICollection{T}"/> rather than <see cref="ISet{T}"/> so we don't have to implement
+ /// a bunch of methods that we aren't really interested in. The <see cref="Keys"/> and <see cref="Values"/>
+ /// properties both return <see cref="ICollection{T}"/>, and while there is no <see cref="EntrySet()"/> method
+ /// or property in .NET, if there were it would certainly return <see cref="ICollection{T}"/>.
/// </summary>
- private class EmptyCharArrayMap<V> : UnmodifiableCharArrayMap<V>
+ public sealed class EntrySet_ : ICollection<KeyValuePair<string, TValue>>
{
- public EmptyCharArrayMap()
-#pragma warning disable 612, 618
- : base(new CharArrayMap<V>(LuceneVersion.LUCENE_CURRENT, 0, false))
-#pragma warning restore 612, 618
+ private readonly CharArrayMap<TValue> outerInstance;
+
+ internal readonly bool allowModify;
+
+ internal EntrySet_(CharArrayMap<TValue> outerInstance, bool allowModify)
{
+ this.outerInstance = outerInstance;
+ this.allowModify = allowModify;
}
- public override bool ContainsKey(char[] text, int off, int len)
+ public IEnumerator GetEnumerator()
{
- if (text == null)
- {
- throw new NullReferenceException();
- }
- return false;
+ return new EntryIterator(outerInstance, allowModify);
}
- public bool ContainsKey(ICharSequence cs)
+ IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator()
{
- if (cs == null)
- {
- throw new NullReferenceException();
- }
- return false;
+ return (IEnumerator<KeyValuePair<string, TValue>>)GetEnumerator();
}
- public override bool ContainsKey(object o)
+ public bool Contains(object o)
{
- if (o == null)
+ if (!(o is KeyValuePair<string, TValue>))
{
- throw new NullReferenceException();
+ return false;
}
- return false;
+ var e = (KeyValuePair<string, TValue>)o;
+ string key = e.Key;
+ TValue val = e.Value;
+ TValue v = outerInstance.Get(key);
+ return v == null ? val == null : v.Equals(val);
}
- public override V Get(char[] text, int off, int len)
+ [Obsolete("Not applicable in this class.")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public bool Remove(KeyValuePair<string, TValue> item)
{
- if (text == null)
- {
- throw new NullReferenceException();
- }
- return default(V);
+ throw new NotSupportedException();
}
- public V Get(ICharSequence cs)
+ public int Count
{
- if (cs == null)
+ get { return outerInstance.count; }
+ }
+
+ public void Clear()
+ {
+ if (!allowModify)
{
- throw new NullReferenceException();
+ throw new NotSupportedException();
}
- return default(V);
+ outerInstance.Clear();
+ }
+
+ #region LUCENENET Added for better .NET support
+
+ public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
+ {
+ outerInstance.CopyTo(array, arrayIndex);
+ }
+
+ [Obsolete("Not applicable in this class.")]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+ public bool Contains(KeyValuePair<string, TValue> item)
+ {
+ return outerInstance.Contains(item);
+ }
+
+ public void Add(KeyValuePair<string, TValue> item)
+ {
+ outerInstance.Add(item);
+ }
+
+ public bool IsReadOnly { get { return !allowModify; } }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder("[");
+
+ IEnumerator<KeyValuePair<string, TValue>> iter1 = new EntryIterator(this.outerInstance, false);
+ while (iter1.MoveNext())
+ {
+ KeyValuePair<string, TValue> entry = iter1.Current;
+ if (sb.Length > 1)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(entry.Key);
+ sb.Append("=");
+ sb.Append(entry.Value);
+ }
+
+ return sb.Append(']').ToString();
+ }
+ #endregion
+ }
+
+ // LUCENENET: Moved UnmodifiableMap static methods to CharArrayMap class
+
+ // LUCENENET: Moved Copy static methods to CharArrayMap class
+
+ /// <summary>
+ /// Returns an empty, unmodifiable map. </summary>
+ public static CharArrayMap<TValue> EmptyMap()
+ {
+ return EMPTY_MAP;
+ }
+
+ // LUCENENET: Moved UnmodifyableCharArraymap to CharArrayMap class
+
+ // LUCENENET: Moved EmptyCharArrayMap to CharArrayMap class
+ }
<TRUNCATED>