| // Lucene version compatibility level 4.8.1 |
| using J2N; |
| using J2N.Globalization; |
| using J2N.Text; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Support; |
| using Lucene.Net.Util; |
| using System; |
| using System.Collections; |
| using System.Collections.Generic; |
| using System.ComponentModel; |
| using System.Diagnostics; |
| using System.Diagnostics.CodeAnalysis; |
| using System.Globalization; |
| using System.Runtime.CompilerServices; |
| using System.Text; |
| using JCG = J2N.Collections.Generic; |
| #nullable enable |
| |
| // LUCENENET specific - this class was significantly refactored from its Java counterpart to look and act more like collections in .NET. |
| |
| namespace Lucene.Net.Analysis.Util |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// A simple class that stores text <see cref="string"/>s as <see cref="T:char[]"/>'s in a |
| /// hash table. Note that this is not a general purpose |
| /// class. For example, it cannot remove items from the |
| /// dictionary, nor does it resize its hash table to be smaller, |
| /// etc. It is designed to be quick to retrieve items |
| /// by <see cref="T:char[]"/> keys without the necessity of converting |
| /// to a <see cref="string"/> first. |
| /// |
| /// <a name="version"></a> |
| /// <para>You must specify the required <see cref="LuceneVersion"/> |
| /// compatibility when creating <see cref="CharArrayDictionary{TValue}"/>: |
| /// <list type="bullet"> |
| /// <item><description> As of 3.1, supplementary characters are |
| /// properly lowercased.</description></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 |
| /// <see cref="CharArrayDictionary{TValue}"/> with the behavior before Lucene |
| /// 3.1 pass a <see cref="LuceneVersion"/> < 3.1 to the constructors. |
| /// </para> |
| /// </summary> |
| [DebuggerDisplay("Count = {Count}, Values = {ToString()}")] |
| [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] |
| [SuppressMessage("CodeQuality", "S3218:Inner class members should not shadow outer class \"static\" or type members", Justification = "Following Microsoft's code style for collections")] |
| [SuppressMessage("CodeQuality", "S1939:Inheritance list should not be redundant", Justification = "Following Microsoft's code style for collections")] |
| public class CharArrayDictionary<TValue> : ICharArrayDictionary, IDictionary<string, TValue>, IDictionary, IReadOnlyDictionary<string, TValue> |
| { |
| // LUCENENET: Made public, renamed Empty |
| /// <summary> |
| /// Returns an empty, read-only dictionary. </summary> |
| [SuppressMessage("Performance", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] |
| [SuppressMessage("Performance", "S3887:Use an immutable collection or reduce the accessibility of the non-private readonly field", Justification = "Collection is immutable")] |
| [SuppressMessage("Performance", "S2386:Use an immutable collection or reduce the accessibility of the public static field", Justification = "Collection is immutable")] |
| public static readonly CharArrayDictionary<TValue> Empty = new CharArrayDictionary.EmptyCharArrayDictionary<TValue>(); |
| |
| private const int INIT_SIZE = 8; |
| private readonly CharacterUtils charUtils; |
| private readonly bool ignoreCase; |
| private int count; |
| 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 MapValue[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator |
| |
| private int version; // LUCENENET specific - protection so mutating the state of the collection causes enumerators to throw exceptions. |
| |
| /// <summary> |
| /// LUCENENET: Moved this from CharArraySet so it doesn't need to know the generic type of CharArrayDictionary |
| /// </summary> |
| internal static readonly MapValue PLACEHOLDER = new MapValue(); |
| |
| bool ICharArrayDictionary.IgnoreCase => ignoreCase; |
| |
| /// <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 <typeparamref name="TValue"/> 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 <typeparamref name="TValue"/>, 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 |
| { |
| [AllowNull] |
| private TValue value = default; |
| |
| [AllowNull] |
| public TValue Value |
| { |
| get => value!; // We are lying here - if this is a reference type, it could be null. But we don't care because IDictionary<TKey, TValue> doesn't care. |
| set => this.value = value; |
| } |
| |
| public MapValue() |
| { } |
| |
| public MapValue([AllowNull] TValue value) |
| { |
| this.value = value; |
| } |
| } |
| |
| /// <summary> |
| /// Create dictionary with enough capacity to hold <paramref name="capacity"/> terms. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// lucene compatibility version - see <see cref="CharArrayDictionary{TValue}"/> for details. </param> |
| /// <param name="capacity"> |
| /// the initial capacity </param> |
| /// <param name="ignoreCase"> |
| /// <c>false</c> if and only if the set should be case sensitive; |
| /// otherwise <c>true</c>. </param> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero.</exception> |
| public CharArrayDictionary(LuceneVersion matchVersion, int capacity, bool ignoreCase) |
| { |
| // LUCENENET: Added guard clause |
| if (capacity < 0) |
| throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum); |
| |
| this.ignoreCase = ignoreCase; |
| var size = INIT_SIZE; |
| while (capacity + (capacity >> 2) > size) |
| { |
| size <<= 1; |
| } |
| keys = new char[size][]; |
| values = new MapValue[size]; |
| this.charUtils = CharacterUtils.GetInstance(matchVersion); |
| this.matchVersion = matchVersion; |
| } |
| |
| /// <summary> |
| /// Creates a dictionary from the mappings in another dictionary. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <see cref="CharArrayDictionary{TValue}"/> for details. </param> |
| /// <param name="collection"> |
| /// a dictionary (<see cref="T:IDictionary{string,V}"/>) whose mappings to be copied. </param> |
| /// <param name="ignoreCase"> |
| /// <c>false</c> if and only if the set should be case sensitive; |
| /// otherwise <c>true</c>. </param> |
| /// <exception cref="ArgumentNullException"><paramref name="collection"/> is <c>null</c>.</exception> |
| public CharArrayDictionary(LuceneVersion matchVersion, IDictionary<string, TValue> collection, bool ignoreCase) |
| : this(matchVersion, collection?.Count ?? 0, ignoreCase) |
| { |
| // LUCENENET: Added guard clause |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var v in collection) |
| { |
| // LUCENENET: S1699: Don't call call protected members in the constructor |
| if (keys[GetSlot(v.Key)] != null) // ContainsKey |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, v.Key)); |
| } |
| SetImpl(v.Key, new MapValue(v.Value)); |
| } |
| } |
| |
| /// <summary> |
| /// Creates a dictionary from the mappings in another dictionary. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <see cref="CharArrayDictionary{TValue}"/> for details. </param> |
| /// <param name="collection"> |
| /// a dictionary (<see cref="T:IDictionary{char[],V}"/>) whose mappings to be copied. </param> |
| /// <param name="ignoreCase"> |
| /// <c>false</c> if and only if the set should be case sensitive; |
| /// otherwise <c>true</c>. </param> |
| /// <exception cref="ArgumentNullException"><paramref name="collection"/> is <c>null</c>.</exception> |
| public CharArrayDictionary(LuceneVersion matchVersion, IDictionary<char[], TValue> collection, bool ignoreCase) |
| : this(matchVersion, collection?.Count ?? 0, ignoreCase) |
| { |
| // LUCENENET: Added guard clause |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var v in collection) |
| { |
| // LUCENENET: S1699: Don't call call protected members in the constructor |
| if (keys[GetSlot(v.Key!, 0, v.Key?.Length ?? 0)] != null) // ContainsKey |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, v.Key)); |
| } |
| SetImpl(v.Key!, new MapValue(v.Value)); |
| } |
| } |
| |
| /// <summary> |
| /// Creates a dictionary from the mappings in another dictionary. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <see cref="CharArrayDictionary{TValue}"/> for details. </param> |
| /// <param name="collection"> |
| /// a dictionary (<see cref="T:IDictionary{ICharSequence,V}"/>) whose mappings to be copied. </param> |
| /// <param name="ignoreCase"> |
| /// <c>false</c> if and only if the set should be case sensitive; |
| /// otherwise <c>true</c>. </param> |
| /// <exception cref="ArgumentNullException"><paramref name="collection"/> is <c>null</c>.</exception> |
| public CharArrayDictionary(LuceneVersion matchVersion, IDictionary<ICharSequence, TValue> collection, bool ignoreCase) |
| : this(matchVersion, collection?.Count ?? 0, ignoreCase) |
| { |
| // LUCENENET: Added guard clause |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var v in collection) |
| { |
| // LUCENENET: S1699: Don't call call protected members in the constructor |
| if (keys[GetSlot(v.Key)] != null) // ContainsKey |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, v.Key)); |
| } |
| SetImpl(v.Key, new MapValue(v.Value)); |
| } |
| } |
| |
| /// <summary> |
| /// Create set from the supplied dictionary (used internally for readonly maps...) |
| /// </summary> |
| internal CharArrayDictionary(CharArrayDictionary<TValue> toCopy) |
| { |
| this.keys = toCopy.keys; |
| this.values = toCopy.values; |
| this.ignoreCase = toCopy.ignoreCase; |
| this.count = toCopy.count; |
| this.charUtils = toCopy.charUtils; |
| this.matchVersion = toCopy.matchVersion; |
| } |
| |
| /// <summary> |
| /// Adds the <see cref="T:KeyValuePair{string, V}.Value"/> for the passed in <see cref="T:KeyValuePair{string, V}.Key"/>. |
| /// Note that the <see cref="T:KeyValuePair{string, V}"/> instance is not added to the dictionary. |
| /// </summary> |
| /// <param name="item">A <see cref="T:KeyValuePair{string, V}"/> whose <see cref="T:KeyValuePair{string, V}.Value"/> |
| /// will be added for the corresponding <see cref="T:KeyValuePair{string, V}.Key"/>. </param> |
| void ICollection<KeyValuePair<string, TValue>>.Add(KeyValuePair<string, TValue> item) |
| { |
| Add(item.Key, item.Value); |
| } |
| |
| /// <summary> |
| /// Adds the <paramref name="value"/> for the passed in <paramref name="text"/>. |
| /// </summary> |
| /// <param name="text">The string-able type to be added/updated in the dictionary.</param> |
| /// <param name="value">The corresponding value for the given <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentException">An element with <paramref name="text"/> already exists in the dictionary.</exception> |
| public virtual void Add(string text, TValue value) |
| { |
| if (ContainsKey(text)) |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, text), nameof(text)); |
| } |
| Set(text, value); |
| } |
| |
| /// <summary> |
| /// Adds the <paramref name="value"/> for the passed in <paramref name="text"/>. |
| /// </summary> |
| /// <param name="text">The string-able type to be added/updated in the dictionary.</param> |
| /// <param name="value">The corresponding value for the given <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentException">An element with <paramref name="text"/> already exists in the dictionary.</exception> |
| public virtual void Add(char[] text, TValue value) |
| { |
| if (ContainsKey(text)) |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, text), nameof(text)); |
| } |
| Set(text, value); |
| } |
| |
| /// <summary> |
| /// Adds the <paramref name="value"/> for the passed in <paramref name="text"/>. |
| /// </summary> |
| /// <param name="text">The string-able type to be added/updated in the dictionary.</param> |
| /// <param name="value">The corresponding value for the given <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| /// <exception cref="ArgumentException">An element with <paramref name="text"/> already exists in the dictionary.</exception> |
| public virtual void Add(ICharSequence text, TValue value) |
| { |
| if (ContainsKey(text)) |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, text), nameof(text)); |
| } |
| Set(text, value); |
| } |
| |
| /// <summary> |
| /// Adds the <paramref name="value"/> for the passed in <paramref name="text"/>. |
| /// </summary> |
| /// <param name="text">The string-able type to be added/updated in the dictionary.</param> |
| /// <param name="value">The corresponding value for the given <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentException">An element with <paramref name="text"/> already exists in the dictionary.</exception> |
| public virtual void Add<T>(T text, TValue value) |
| { |
| if (ContainsKey(text)) |
| { |
| throw new ArgumentException(string.Format(SR.Argument_AddingDuplicate, text), nameof(text)); |
| } |
| Set(text, value); |
| } |
| |
| /// <summary> |
| /// Returns an unmodifiable <see cref="CharArrayDictionary{TValue}"/>. This allows to provide |
| /// unmodifiable views of internal dictionary for "read-only" use. |
| /// </summary> |
| /// <returns> an new unmodifiable <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| // LUCENENET specific - allow .NET-like syntax for creating immutable collections |
| public CharArrayDictionary<TValue> AsReadOnly() => AsReadOnlyImpl(); |
| |
| private protected virtual CharArrayDictionary<TValue> AsReadOnlyImpl() // Hack so we can make it virtual |
| { |
| return new CharArrayDictionary.ReadOnlyCharArrayDictionary<TValue>(this); |
| } |
| |
| /// <summary> |
| /// Clears all entries in this dictionary. This method is supported for reusing, but not |
| /// <see cref="IDictionary{TKey, TValue}.Remove(TKey)"/>. |
| /// </summary> |
| public virtual void Clear() |
| { |
| version++; |
| count = 0; |
| Arrays.Fill(keys, null); |
| Arrays.Fill(values, null); |
| } |
| |
| /// <summary> |
| /// Not supported. |
| /// </summary> |
| bool ICollection<KeyValuePair<string, TValue>>.Contains(KeyValuePair<string, TValue> item) |
| { |
| throw UnsupportedOperationException.Create(); |
| } |
| |
| /// <summary> |
| /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <paramref name="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 <paramref name="array"/> at which copying begins.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException">The number of elements in the source is greater |
| /// than the available space from <paramref name="arrayIndex"/> to the end of the destination array.</exception> |
| // LUCENENET: Generally, it makes more sense to use the Enuerator explicitly so we have access to the underling char[] and so |
| // we don't have to new up a KeyValuePair<string, TValue> just for the sake of reading data. |
| private void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex) |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (arrayIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (count > array.Length - arrayIndex) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| using var iter = GetEnumerator(); |
| for (int i = arrayIndex; iter.MoveNext(); i++) |
| { |
| array[i] = new KeyValuePair<string, TValue>(iter.CurrentKeyString, iter.CurrentValue!); |
| } |
| } |
| |
| void ICollection<KeyValuePair<string, TValue>>.CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex) => CopyTo(array, arrayIndex); |
| |
| |
| /// <summary> |
| /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <paramref name="index"/>. |
| /// 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="index">A 32-bit integer that represents the index in <paramref name="array"/> at which copying begins.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException">The number of elements in the source is greater |
| /// than the available space from <paramref name="index"/> to the end of the destination array.</exception> |
| internal void CopyTo(KeyValuePair<char[], TValue>[] array, int index) // internal for testing |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (index < 0) |
| throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (count > array.Length - index) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| using var iter = GetEnumerator(); |
| for (int i = index; iter.MoveNext(); i++) |
| { |
| array[i] = new KeyValuePair<char[], TValue>((char[])iter.CurrentKey.Clone(), iter.CurrentValue!); |
| } |
| } |
| |
| /// <summary> |
| /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <paramref name="index"/>. |
| /// 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="index">A 32-bit integer that represents the index in <paramref name="array"/> at which copying begins.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException">The number of elements in the source is greater |
| /// than the available space from <paramref name="index"/> to the end of the destination array.</exception> |
| internal void CopyTo(KeyValuePair<ICharSequence, TValue>[] array, int index) // internal for testing |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (index < 0) |
| throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (count > array.Length - index) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| using var iter = GetEnumerator(); |
| for (int i = index; iter.MoveNext(); i++) |
| { |
| array[i] = new KeyValuePair<ICharSequence, TValue>(((char[])iter.CurrentKey.Clone()).AsCharSequence(), iter.CurrentValue!); |
| } |
| } |
| |
| /// <summary> |
| /// <c>true</c> if the <paramref name="length"/> chars of <paramref name="text"/> starting at <paramref name="startIndex"/> |
| /// are in the <see cref="Keys"/> |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| public virtual bool ContainsKey(char[] text, int startIndex, int length) |
| { |
| return keys[GetSlot(text, startIndex, length)] != null; |
| } |
| |
| /// <summary> |
| /// <c>true</c> if the entire <see cref="Keys"/> is the same as the |
| /// <paramref name="text"/> <see cref="T:char[]"/> being passed in; |
| /// otherwise <c>false</c>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool ContainsKey(char[] text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| return keys[GetSlot(text, 0, text.Length)] != null; |
| } |
| |
| /// <summary> |
| /// <c>true</c> if the <paramref name="text"/> <see cref="string"/> is in the <see cref="Keys"/>; |
| /// otherwise <c>false</c> |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool ContainsKey(string text) |
| { |
| return keys[GetSlot(text)] != null; |
| } |
| |
| /// <summary> |
| /// <c>true</c> if the <paramref name="text"/> <see cref="ICharSequence"/> is in the <see cref="Keys"/>; |
| /// otherwise <c>false</c> |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| public virtual bool ContainsKey(ICharSequence text) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (text is CharArrayCharSequence charArrayCs) |
| return ContainsKey(charArrayCs.Value!); |
| if (text is StringBuilderCharSequence stringBuilderCs) |
| return ContainsKey(stringBuilderCs.Value!.ToString()); // LUCENENET: Indexing into a StringBuilder is slow, so materialize |
| |
| return keys[GetSlot(text)] != null; |
| } |
| |
| |
| /// <summary> |
| /// <c>true</c> if the <paramref name="text"/> <see cref="object.ToString()"/> (in the invariant culture) |
| /// is in the <see cref="Keys"/>; otherwise <c>false</c> |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool ContainsKey<T>(T text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| |
| if (text is string str) |
| return ContainsKey(str); |
| if (text is char[] charArray) |
| return ContainsKey(charArray, 0, charArray.Length); |
| if (text is ICharSequence cs) |
| return ContainsKey(cs); |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| return ContainsKey(s); |
| else |
| return ContainsKey(chars); |
| } |
| |
| #region Get |
| |
| /// <summary> |
| /// Returns the value of the mapping of <paramref name="length"/> chars of <paramref name="text"/> |
| /// starting at <paramref name="startIndex"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| /// <exception cref="KeyNotFoundException">The effective text is not found in the dictionary.</exception> |
| internal virtual TValue Get(char[] text, int startIndex, int length, bool throwIfNotFound = true) |
| { |
| MapValue? value = values[GetSlot(text, startIndex, length)]; |
| if (value is not null) |
| { |
| return value.Value; |
| } |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, new string(text, startIndex, length))); |
| return default!; |
| } |
| |
| /// <summary> |
| /// Returns the value of the mapping of the chars inside this <paramref name="text"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="KeyNotFoundException"><paramref name="text"/> is not found in the dictionary.</exception> |
| internal virtual TValue Get(char[] text, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| MapValue? value = values[GetSlot(text, 0, text.Length)]; |
| if (value is not null) |
| { |
| return value.Value; |
| } |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, new string(text))); |
| return default!; |
| } |
| |
| /// <summary> |
| /// Returns the value of the mapping of the chars inside this <see cref="ICharSequence"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| /// <exception cref="KeyNotFoundException"><paramref name="text"/> is not found in the dictionary.</exception> |
| internal virtual TValue Get(ICharSequence text, bool throwIfNotFound = true) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (text is StringCharSequence strCs) |
| return Get(strCs.Value!, throwIfNotFound); |
| if (text is CharArrayCharSequence charArrayCs) |
| return Get(charArrayCs.Value!, throwIfNotFound); |
| if (text is StringBuilderCharSequence stringBuilderCs) |
| return Get(stringBuilderCs.Value!.ToString(), throwIfNotFound); // LUCENENET: Indexing into a StringBuilder is slow, so materialize |
| |
| var value = values[GetSlot(text)]; |
| if (value is not null) |
| { |
| return value.Value; |
| } |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, text)); |
| return default!; |
| } |
| |
| /// <summary> |
| /// Returns the value of the mapping of the chars inside this <see cref="string"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="KeyNotFoundException"><paramref name="text"/> is not found in the dictionary.</exception> |
| internal virtual TValue Get(string text, bool throwIfNotFound = true) |
| { |
| var value = values[GetSlot(text)]; |
| if (value is not null) |
| { |
| return value.Value; |
| } |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, text)); |
| return default!; |
| } |
| |
| /// <summary> |
| /// Returns the value of the mapping of the chars inside this <see cref="object.ToString()"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="KeyNotFoundException"><paramref name="text"/> is not found in the dictionary.</exception> |
| internal virtual TValue Get<T>(T text, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // 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 (text is string str) |
| return Get(str, throwIfNotFound); |
| if (text is char[] charArray) |
| return Get(charArray, 0, charArray.Length, throwIfNotFound); |
| if (text is ICharSequence cs) |
| return Get(cs, throwIfNotFound); |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| return Get(s, throwIfNotFound); |
| else |
| return Get(chars, throwIfNotFound); |
| } |
| |
| #endregion Get |
| |
| #region GetSlot |
| |
| private int GetSlot(char[] text, int startIndex, int length) |
| { |
| int code = GetHashCode(text, startIndex, length); |
| int pos = code & (keys.Length - 1); |
| char[] text2 = keys[pos]; |
| if (text2 != null && !Equals(text, startIndex, length, text2)) |
| { |
| int inc = ((code >> 8) + code) | 1; |
| do |
| { |
| code += inc; |
| pos = code & (keys.Length - 1); |
| text2 = keys[pos]; |
| } while (text2 != null && !Equals(text, startIndex, length, text2)); |
| } |
| return pos; |
| } |
| |
| /// <summary> |
| /// Returns <c>true</c> if the <see cref="ICharSequence"/> is in the set. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| private int GetSlot(ICharSequence text) |
| { |
| int code = GetHashCode(text); |
| int pos = code & (keys.Length - 1); |
| char[] text2 = keys[pos]; |
| if (text2 != null && !Equals(text, text2)) |
| { |
| int inc = ((code >> 8) + code) | 1; |
| do |
| { |
| code += inc; |
| pos = code & (keys.Length - 1); |
| text2 = keys[pos]; |
| } while (text2 != null && !Equals(text, text2)); |
| } |
| return pos; |
| } |
| |
| /// <summary> |
| /// Returns <c>true</c> if the <see cref="string"/> is in the set. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| private int GetSlot(string text) |
| { |
| int code = GetHashCode(text); |
| int pos = code & (keys.Length - 1); |
| char[] text2 = keys[pos]; |
| if (text2 != null && !Equals(text, text2)) |
| { |
| int inc = ((code >> 8) + code) | 1; |
| do |
| { |
| code += inc; |
| pos = code & (keys.Length - 1); |
| text2 = keys[pos]; |
| } while (text2 != null && !Equals(text, text2)); |
| } |
| return pos; |
| } |
| |
| #endregion GetSlot |
| |
| #region Put (value) |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// <para/> |
| /// <b>Note:</b> The <see cref="this[char[]]"/> setter is more efficient than this method if |
| /// the <paramref name="previousValue"/> is not required. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="startIndex">The position of the <paramref name="text"/> where the target text begins.</param> |
| /// <param name="length">The total length of the <paramref name="text"/>.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <param name="previousValue">The previous value associated with the text, or the default for the type of <paramref name="value"/> |
| /// parameter if there was no mapping for <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the mapping was added, <c>false</c> if the text already existed. The <paramref name="previousValue"/> |
| /// will be populated if the result is <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| public virtual bool Put(char[] text, int startIndex, int length, TValue value, [MaybeNullWhen(returnValue: true)] out TValue previousValue) // LUCENENET: Refactored to use out value to support value types |
| { |
| MapValue? oldValue = PutImpl(text, startIndex, length, new MapValue(value)); |
| if (oldValue is not null) |
| { |
| previousValue = oldValue.Value; |
| return false; |
| } |
| previousValue = default; |
| return true; |
| } |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// The user should never modify this text array after calling this method. |
| /// <para/> |
| /// <b>Note:</b> The <see cref="this[char[]]"/> setter is more efficient than this method if |
| /// the <paramref name="previousValue"/> is not required. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <param name="previousValue">The previous value associated with the text, or the default for the type of <paramref name="value"/> |
| /// parameter if there was no mapping for <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the mapping was added, <c>false</c> if the text already existed. The <paramref name="previousValue"/> |
| /// will be populated if the result is <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool Put(char[] text, TValue value, [MaybeNullWhen(returnValue: true)] out TValue previousValue) // LUCENENET: Refactored to use out value to support value types |
| { |
| MapValue? oldValue = PutImpl(text, new MapValue(value)); |
| if (oldValue is not null) |
| { |
| previousValue = oldValue.Value; |
| return false; |
| } |
| previousValue = default; |
| return true; |
| } |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// <para/> |
| /// <b>Note:</b> The <see cref="this[string]"/> setter is more efficient than this method if |
| /// the <paramref name="previousValue"/> is not required. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <param name="previousValue">The previous value associated with the text, or the default for the type of <paramref name="value"/> |
| /// parameter if there was no mapping for <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the mapping was added, <c>false</c> if the text already existed. The <paramref name="previousValue"/> |
| /// will be populated if the result is <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool Put(string text, TValue value, [MaybeNullWhen(returnValue: true)] out TValue previousValue) // LUCENENET: Refactored to use out value to support value types |
| { |
| MapValue? oldValue = PutImpl(text, new MapValue(value)); |
| if (oldValue is not null) |
| { |
| previousValue = oldValue.Value; |
| return false; |
| } |
| previousValue = default; |
| return true; |
| } |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// <para/> |
| /// <b>Note:</b> The <see cref="this[ICharSequence]"/> setter is more efficient than this method if |
| /// the <paramref name="previousValue"/> is not required. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <param name="previousValue">The previous value associated with the text, or the default for the type of <paramref name="value"/> |
| /// parameter if there was no mapping for <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the mapping was added, <c>false</c> if the text already existed. The <paramref name="previousValue"/> |
| /// will be populated if the result is <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| public virtual bool Put(ICharSequence text, TValue value, [MaybeNullWhen(returnValue: true)] out TValue previousValue) // LUCENENET: Refactored to use out value to support value types |
| { |
| MapValue? oldValue = PutImpl(text, new MapValue(value)); |
| if (oldValue is not null) |
| { |
| previousValue = oldValue.Value; |
| return false; |
| } |
| previousValue = default; |
| return true; |
| } |
| |
| /// <summary> |
| /// Add the given mapping using the <see cref="object.ToString()"/> representation |
| /// of <paramref name="text"/> in the <see cref="CultureInfo.InvariantCulture"/>. |
| /// <para/> |
| /// <b>Note:</b> The <see cref="this[object]"/> setter is more efficient than this method if |
| /// the <paramref name="previousValue"/> is not required. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <param name="previousValue">The previous value associated with the text, or the default for the type of <paramref name="value"/> |
| /// parameter if there was no mapping for <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the mapping was added, <c>false</c> if the text already existed. The <paramref name="previousValue"/> |
| /// will be populated if the result is <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool Put<T>(T text, TValue value, [MaybeNullWhen(returnValue: true)] out TValue previousValue) // LUCENENET: Refactored to use out value to support value types |
| { |
| MapValue? oldValue = PutImpl(text, new MapValue(value)); |
| if (oldValue is not null) |
| { |
| previousValue = oldValue.Value; |
| return false; |
| } |
| previousValue = default; |
| return true; |
| } |
| |
| #endregion Put (value) |
| |
| #region PutImpl |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private MapValue? PutImpl(ICharSequence text, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (text is CharArrayCharSequence charArrayCs) |
| return PutImpl(charArrayCs.Value ?? Array.Empty<char>(), value); |
| if (text is StringBuilderCharSequence stringBuilderCs) // LUCENENET: Indexing into a StringBuilder is slow, so materialize |
| { |
| var sb = stringBuilderCs.Value!; |
| char[] result = new char[sb.Length]; |
| sb.CopyTo(sourceIndex: 0, result, destinationIndex: 0, sb.Length); |
| return PutImpl(result, value); |
| } |
| |
| int length = text.Length; |
| char[] buffer = new char[length]; |
| for (int i = 0; i < length; i++) |
| { |
| buffer[i] = text[i]; |
| } |
| return PutImpl(buffer, value); |
| } |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private MapValue? PutImpl<T>(T text, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // 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 (text is string str) |
| return PutImpl(str, value); |
| if (text is char[] charArray) |
| return PutImpl(charArray, value); |
| if (text is ICharSequence cs) |
| return PutImpl(cs, value); |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| return PutImpl(s, value); |
| else |
| return PutImpl(chars, value); |
| } |
| |
| /// <summary> |
| /// Add the given mapping. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private MapValue? PutImpl(string text, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // LUCENENET specific - only allocate char array if it is required. |
| if (ignoreCase) |
| { |
| return PutImpl(text.ToCharArray(), value); |
| } |
| version++; |
| int slot = GetSlot(text); |
| if (keys[slot] != null) |
| { |
| MapValue oldValue = values[slot]; |
| values[slot] = value; |
| return oldValue; |
| } |
| keys[slot] = text.ToCharArray(); |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| |
| return null; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private MapValue? PutImpl(char[] text, int startIndex, int length, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| if (startIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (length < 0) |
| throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (startIndex > text.Length - length) // Checks for int overflow |
| throw new ArgumentException(SR.ArgumentOutOfRange_IndexLength); |
| |
| version++; |
| |
| if (ignoreCase) |
| { |
| charUtils.ToLower(text, startIndex, length); |
| } |
| int slot = GetSlot(text, startIndex, length); |
| if (keys[slot] != null) |
| { |
| MapValue oldValue = values[slot]; |
| values[slot] = value; |
| return oldValue; |
| } |
| keys[slot] = text.AsSpan(startIndex, length).ToArray(); |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| |
| return null; |
| } |
| |
| /// <summary> |
| /// LUCENENET specific. Centralizes the logic between 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 <typeparamref name="TValue"/> type. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private MapValue? PutImpl(char[] text, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| version++; |
| |
| if (ignoreCase) |
| { |
| charUtils.ToLower(text, 0, text.Length); |
| } |
| int slot = GetSlot(text, 0, text.Length); |
| if (keys[slot] != null) |
| { |
| MapValue oldValue = values[slot]; |
| values[slot] = value; |
| return oldValue; |
| } |
| keys[slot] = text; |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| |
| return null; |
| } |
| |
| #endregion PutImpl |
| |
| #region Set |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <paramref name="text"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(char[] text, int startIndex, int length) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, startIndex, length, PLACEHOLDER); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <paramref name="text"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(char[] text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, PLACEHOLDER); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="ICharSequence"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(ICharSequence text) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, PLACEHOLDER); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="string"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(string text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, PLACEHOLDER); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="object.ToString()"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| internal virtual void Set<T>(T text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // 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 (text is string str) |
| { |
| Set(str); |
| return; |
| } |
| if (text is char[] charArray) |
| { |
| Set(charArray); |
| return; |
| } |
| if (text is ICharSequence cs) |
| { |
| Set(cs); |
| return; |
| } |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| { |
| Set(s); |
| return; |
| } |
| |
| Set(chars); |
| } |
| |
| void ICharArrayDictionary.Set(char[] text, int startIndex, int length) => Set(text, startIndex, length); |
| void ICharArrayDictionary.Set(char[] text) => Set(text); |
| void ICharArrayDictionary.Set(ICharSequence text) => Set(text); |
| void ICharArrayDictionary.Set<T>(T text) => Set(text); |
| void ICharArrayDictionary.Set(string text) => Set(text); |
| |
| #endregion Set |
| |
| #region Set (value) |
| |
| /// <summary> |
| /// Sets the value of the mapping of <paramref name="length"/> chars of <paramref name="text"/> |
| /// starting at <paramref name="startIndex"/>. |
| /// <para/> |
| /// If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// </summary> |
| /// <param name="text">A text with which the specified <paramref name="value"/> is associated.</param> |
| /// <param name="startIndex">The position of the <paramref name="text"/> where the target text begins.</param> |
| /// <param name="length">The total length of the <paramref name="text"/>.</param> |
| /// <param name="value">The value to be associated with the specified <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(char[] text, int startIndex, int length, TValue? value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, startIndex, length, new MapValue(value)); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <paramref name="text"/>. |
| /// <para/> |
| /// If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// The user should never modify this text array after calling this method. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(char[] text, TValue? value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, new MapValue(value)); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="ICharSequence"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(ICharSequence text, TValue? value) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, new MapValue(value)); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="string"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set(string text, TValue? value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| SetImpl(text, new MapValue(value)); |
| } |
| |
| /// <summary> |
| /// Sets the value of the mapping of the chars inside this <see cref="object.ToString()"/>. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal virtual void Set<T>(T text, TValue? value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // 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 (text is string str) |
| { |
| Set(str, value); |
| return; |
| } |
| if (text is char[] charArray) |
| { |
| Set(charArray, 0, charArray.Length, value); |
| return; |
| } |
| if (text is ICharSequence cs) |
| { |
| Set(cs, value); |
| return; |
| } |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| { |
| Set(s, value); |
| return; |
| } |
| |
| Set(chars, value); |
| } |
| |
| #endregion Set (value) |
| |
| #region SetImpl |
| |
| /// <summary> |
| /// LUCENENET specific. Like PutImpl, but doesn't have a return value or lookup to get the old value. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private void SetImpl(ICharSequence text, MapValue value) |
| { |
| // LUCENENET: Added guard clause |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (text is CharArrayCharSequence charArrayCs) |
| { |
| SetImpl(charArrayCs.Value ?? Array.Empty<char>(), value); |
| return; |
| } |
| if (text is StringBuilderCharSequence stringBuilderCs) // LUCENENET: Indexing into a StringBuilder is slow, so materialize |
| { |
| var sb = stringBuilderCs.Value!; |
| char[] result = new char[sb.Length]; |
| sb.CopyTo(sourceIndex: 0, result, destinationIndex: 0, sb.Length); |
| SetImpl(result, value); |
| return; |
| } |
| |
| int length = text.Length; |
| char[] buffer = new char[length]; |
| for (int i = 0; i < length; i++) |
| { |
| buffer[i] = text[i]; |
| } |
| |
| SetImpl(buffer, value); |
| } |
| |
| /// <summary> |
| /// LUCENENET specific. Like PutImpl, but doesn't have a return value or lookup to get the old value. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private void SetImpl(string text, MapValue value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // LUCENENET specific - only allocate char array if it is required. |
| if (ignoreCase) |
| { |
| SetImpl(text.ToCharArray(), value); |
| return; |
| } |
| version++; |
| int slot = GetSlot(text); |
| if (keys[slot] != null) |
| { |
| values[slot] = value; |
| return; |
| } |
| keys[slot] = text.ToCharArray(); |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| } |
| |
| /// <summary> |
| /// LUCENENET specific. Like PutImpl, but doesn't have a return value or lookup to get the old value. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private void SetImpl(char[] text, MapValue value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| version++; |
| if (ignoreCase) |
| { |
| charUtils.ToLower(text, 0, text.Length); |
| } |
| int slot = GetSlot(text, 0, text.Length); |
| if (keys[slot] != null) |
| { |
| values[slot] = value; |
| return; |
| } |
| keys[slot] = text; |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| } |
| |
| /// <summary> |
| /// LUCENENET specific. Like PutImpl, but doesn't have a return value or lookup to get the old value. |
| /// </summary> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private void SetImpl(char[] text, int startIndex, int length, MapValue value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| if (startIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (length < 0) |
| throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (startIndex > text.Length - length) // Checks for int overflow |
| throw new ArgumentException(SR.ArgumentOutOfRange_IndexLength); |
| |
| version++; |
| if (ignoreCase) |
| { |
| charUtils.ToLower(text, startIndex, length); |
| } |
| int slot = GetSlot(text, startIndex, length); |
| if (keys[slot] != null) |
| { |
| values[slot] = value; |
| return; |
| } |
| keys[slot] = text.AsSpan(startIndex, length).ToArray(); |
| values[slot] = value; |
| count++; |
| |
| if (count + (count >> 2) > keys.Length) |
| { |
| Rehash(); |
| } |
| } |
| |
| #endregion SetImpl |
| |
| #region PutAll |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IDictionary{char[],TValue}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(char[], TValue?)"/> operation once for each entry. |
| /// <para/> |
| /// If ignoreCase is <c>true</c> for this dictionary, the text arrays will be directly modified. |
| /// The user should never modify the text arrays after calling this method. |
| /// </summary> |
| /// <param name="collection">A dictionary of values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll(IDictionary<char[], TValue> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IDictionary{string,TValue}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(string, TValue?)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">A dictionary of values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll(IDictionary<string, TValue> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IDictionary{ICharSequence,TValue}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(ICharSequence, TValue?)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">A dictionary of values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection has a <c>null</c> text. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The text's <see cref="ICharSequence.HasValue"/> property for a given element in the collection returns <c>false</c>. |
| /// </exception> |
| public virtual void PutAll(IDictionary<ICharSequence, TValue> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IDictionary{T,TValue}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set{T}(T, TValue?)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">A dictionary of values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll<T>(IDictionary<T, TValue> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{char[],TValue}}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(char[], TValue?)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">The values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll(IEnumerable<KeyValuePair<char[], TValue>> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{string,TValue}}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(string, TValue)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">The values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll(IEnumerable<KeyValuePair<string, TValue>> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{ICharSequence,TValue}}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set(ICharSequence, TValue)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">The values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection has a <c>null</c> text. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The text's <see cref="ICharSequence.HasValue"/> property for a given element in the collection returns <c>false</c>. |
| /// </exception> |
| public virtual void PutAll(IEnumerable<KeyValuePair<ICharSequence, TValue>> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| foreach (var kvp in collection) |
| { |
| Set(kvp.Key, kvp.Value); |
| } |
| } |
| |
| /// <summary> |
| /// This implementation enumerates over the specified <see cref="T:IEnumerable{KeyValuePair{TKey,TValue}}"/>'s |
| /// entries, and calls this dictionary's <see cref="Set{T}(T, TValue?)"/> operation once for each entry. |
| /// </summary> |
| /// <param name="collection">The values to add/update in the current dictionary.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="collection"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// An element in the collection is <c>null</c>. |
| /// </exception> |
| public virtual void PutAll<T>(IEnumerable<KeyValuePair<T, TValue>> collection) |
| { |
| if (collection is null) |
| throw new ArgumentNullException(nameof(collection)); |
| |
| #if FEATURE_SPANFORMATTABLE |
| Span<char> buffer = stackalloc char[256]; |
| #else |
| Span<char> buffer = stackalloc char[1]; |
| #endif |
| |
| foreach (var kvp in collection) |
| { |
| // Convert the item to chars in the invariant culture |
| var returnType = CharArrayDictionary.ConvertObjectToChars(kvp.Key, out char[] chars, out string s, buffer); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| Set(s, kvp.Value); |
| else |
| Set(chars, kvp.Value); |
| } |
| } |
| |
| #endregion |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private void Rehash() |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(keys.Length == values.Length); |
| int newSize = 2 * keys.Length; |
| char[][] oldkeys = keys; |
| MapValue[] oldvalues = values; |
| keys = new char[newSize][]; |
| values = new MapValue[newSize]; |
| |
| for (int i = 0; i < oldkeys.Length; i++) |
| { |
| char[] text = oldkeys[i]; |
| if (text != null) |
| { |
| // todo: could be faster... no need to compare strings on collision |
| int slot = GetSlot(text, 0, text.Length); |
| keys[slot] = text; |
| values[slot] = oldvalues[i]; |
| } |
| } |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private bool Equals(char[] text1, int startIndex, int length, char[] text2) |
| { |
| if (length != text2.Length) |
| { |
| return false; |
| } |
| int limit = startIndex + length; |
| if (ignoreCase) |
| { |
| for (int i = 0; i < length;) |
| { |
| var codePointAt = charUtils.CodePointAt(text1, startIndex + i, limit); |
| if (Character.ToLower(codePointAt, CultureInfo.InvariantCulture) != charUtils.CodePointAt(text2, i, text2.Length)) // LUCENENET specific - need to use invariant culture to match Java |
| { |
| return false; |
| } |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| else |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (text1[startIndex + i] != text2[i]) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private bool Equals(ICharSequence text1, char[] text2) |
| { |
| int length = text1.Length; |
| if (length != text2.Length) |
| { |
| return false; |
| } |
| if (ignoreCase) |
| { |
| for (int i = 0; i < length;) |
| { |
| int codePointAt = charUtils.CodePointAt(text1, i); |
| if (Character.ToLower(codePointAt, CultureInfo.InvariantCulture) != charUtils.CodePointAt(text2, i, text2.Length)) // LUCENENET specific - need to use invariant culture to match Java |
| { |
| return false; |
| } |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| else |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (text1[i] != text2[i]) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private bool Equals(string text1, char[] text2) |
| { |
| int length = text1.Length; |
| if (length != text2.Length) |
| { |
| return false; |
| } |
| if (ignoreCase) |
| { |
| for (int i = 0; i < length;) |
| { |
| int codePointAt = charUtils.CodePointAt(text1, i); |
| if (Character.ToLower(codePointAt, CultureInfo.InvariantCulture) != charUtils.CodePointAt(text2, i, text2.Length)) // LUCENENET specific - need to use invariant culture to match Java |
| { |
| return false; |
| } |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| else |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (text1[i] != text2[i]) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /// <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="T:IDictionary{object,V}"/> that contains |
| /// the same text value pairs as the current dictionary</returns> |
| public override bool Equals(object? obj) |
| { |
| if (obj is null) |
| return false; |
| if (obj is not IDictionary<string, TValue> other) |
| return false; |
| if (this.Count != other.Count) |
| return false; |
| |
| if (obj is CharArrayDictionary<TValue> charArrayDictionary) |
| { |
| using var iter = charArrayDictionary.GetEnumerator(); |
| while (iter.MoveNext()) |
| { |
| if (!this.TryGetValue(iter.CurrentKey, out TValue? value)) |
| return false; |
| |
| if (!JCG.EqualityComparer<TValue>.Default.Equals(value!, iter.Current.Value)) |
| return false; |
| } |
| } |
| else |
| { |
| using var iter = other.GetEnumerator(); |
| while (iter.MoveNext()) |
| { |
| if (!this.TryGetValue(iter.Current.Key, out TValue? value)) |
| return false; |
| |
| if (!JCG.EqualityComparer<TValue>.Default.Equals(value!, 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() |
| { |
| unchecked |
| { |
| const int PRIME = 31; // arbitrary prime |
| int hash = PRIME; |
| using (var iter = GetEnumerator()) |
| { |
| while (iter.MoveNext()) |
| { |
| hash = (hash * PRIME) ^ iter.CurrentKeyString.GetHashCode(); |
| TValue? value = iter.CurrentValue; |
| hash = (hash * PRIME) ^ |
| (value is null ? 0 : JCG.EqualityComparer<TValue>.Default.GetHashCode(value)); |
| } |
| } |
| |
| return hash; |
| } |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private int GetHashCode(char[] text, int startIndex, int length) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| if (startIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (length < 0) |
| throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (startIndex > text.Length - length) // Checks for int overflow |
| throw new ArgumentException(SR.ArgumentOutOfRange_IndexLength); |
| |
| int code = 0; |
| int stop = startIndex + length; |
| if (ignoreCase) |
| { |
| for (int i = startIndex; i < stop;) |
| { |
| unchecked |
| { |
| int codePointAt = charUtils.CodePointAt(text, i, stop); |
| code = code * 31 + Character.ToLower(codePointAt, CultureInfo.InvariantCulture); // LUCENENET specific - need to use invariant culture to match Java |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| } |
| else |
| { |
| for (int i = startIndex; i < stop; i++) |
| { |
| unchecked |
| { |
| code = code * 31 + text[i]; |
| } |
| } |
| } |
| return code; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private int GetHashCode(ICharSequence text) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| |
| int code = 0; |
| int length = text.Length; |
| if (ignoreCase) |
| { |
| for (int i = 0; i < length;) |
| { |
| unchecked |
| { |
| int codePointAt = charUtils.CodePointAt(text, i); |
| code = code * 31 + Character.ToLower(codePointAt, CultureInfo.InvariantCulture); // LUCENENET specific - need to use invariant culture to match Java |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| } |
| else |
| { |
| for (int i = 0; i < length; i++) |
| { |
| unchecked |
| { |
| code = code * 31 + text[i]; |
| } |
| } |
| } |
| return code; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private int GetHashCode(string text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| |
| int code = 0; |
| int length = text.Length; |
| if (ignoreCase) |
| { |
| for (int i = 0; i < length;) |
| { |
| unchecked |
| { |
| int codePointAt = charUtils.CodePointAt(text, i); |
| code = code * 31 + Character.ToLower(codePointAt, CultureInfo.InvariantCulture); // LUCENENET specific - need to use invariant culture to match Java |
| i += Character.CharCount(codePointAt); |
| } |
| } |
| } |
| else |
| { |
| for (int i = 0; i < length; i++) |
| { |
| unchecked |
| { |
| code = code * 31 + text[i]; |
| } |
| } |
| } |
| return code; |
| } |
| |
| #region For .NET Support LUCENENET |
| |
| /// <summary> |
| /// The Lucene version corresponding to the compatibility behavior |
| /// that this instance emulates |
| /// </summary> |
| public virtual LuceneVersion MatchVersion => matchVersion; |
| |
| /// <summary> |
| /// Adds a placeholder with the given <paramref name="text"/> as the text. |
| /// Primarily for internal use by <see cref="CharArraySet"/>. |
| /// <para/> |
| /// <b>NOTE:</b> If <c>ignoreCase</c> is <c>true</c> for this <see cref="CharArrayDictionary{TValue}"/>, the text array will be directly modified. |
| /// </summary> |
| /// <param name="text">A key with which the placeholder is associated.</param> |
| /// <param name="startIndex">The position of the <paramref name="text"/> where the target text begins.</param> |
| /// <param name="length">The total length of the <paramref name="text"/>.</param> |
| /// <returns><c>true</c> if the text was added, <c>false</c> if the text already existed.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| internal virtual bool Put(char[] text, int startIndex, int length) |
| { |
| return PutImpl(text, startIndex, length, PLACEHOLDER) is null; |
| } |
| |
| /// <summary> |
| /// Adds a placeholder with the given <paramref name="text"/> as the text. |
| /// Primarily for internal use by <see cref="CharArraySet"/>. |
| /// <para/> |
| /// <b>NOTE:</b> If <c>ignoreCase</c> is <c>true</c> for this <see cref="CharArrayDictionary{TValue}"/>, the text array will be directly modified. |
| /// The user should never modify this text array after calling this method. |
| /// </summary> |
| /// <returns><c>true</c> if the text was added, <c>false</c> if the text already existed.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| internal virtual bool Put(char[] text) |
| { |
| return PutImpl(text, PLACEHOLDER) is null; |
| } |
| |
| /// <summary> |
| /// Adds a placeholder with the given <paramref name="text"/> as the text. |
| /// Primarily for internal use by <see cref="CharArraySet"/>. |
| /// </summary> |
| /// <returns><c>true</c> if the text was added, <c>false</c> if the text already existed.</returns> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>.</exception> |
| internal virtual bool Put(ICharSequence text) |
| { |
| return PutImpl(text, PLACEHOLDER) is null; |
| } |
| |
| /// <summary> |
| /// Adds a placeholder with the given <paramref name="text"/> as the text. |
| /// Primarily for internal use by <see cref="CharArraySet"/>. |
| /// </summary> |
| /// <returns><c>true</c> if the text was added, <c>false</c> if the text already existed.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| internal virtual bool Put(string text) |
| { |
| return PutImpl(text, PLACEHOLDER) is null; |
| } |
| |
| /// <summary> |
| /// Adds a placeholder with the given <paramref name="text"/> as the text. |
| /// Primarily for internal use by <see cref="CharArraySet"/>. |
| /// </summary> |
| /// <returns><c>true</c> if the text was added, <c>false</c> if the text already existed.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| internal virtual bool Put<T>(T text) |
| { |
| return PutImpl(text, PLACEHOLDER) is null; |
| } |
| |
| bool ICharArrayDictionary.Put(char[] text, int startIndex, int length) => Put(text, startIndex, length); |
| bool ICharArrayDictionary.Put(char[] text) => Put(text); |
| bool ICharArrayDictionary.Put(string text) => Put(text); |
| bool ICharArrayDictionary.Put(ICharSequence text) => Put(text); |
| bool ICharArrayDictionary.Put<T>(T text) => Put(text); |
| |
| /// <summary> |
| /// Returns a copy of the current <see cref="CharArrayDictionary{TValue}"/> as a new instance of <see cref="CharArrayDictionary{TValue}"/>. |
| /// Preserves the value of <c>matchVersion</c> and <c>ignoreCase</c> from the current instance. |
| /// </summary> |
| /// <returns> A copy of the current <see cref="CharArrayDictionary{TValue}"/> as a <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| // LUCENENET specific - allow .NET-like syntax for copying CharArrayDictionary |
| public virtual CharArrayDictionary<TValue> ToCharArrayDictionary() |
| { |
| return CharArrayDictionary.Copy(this.matchVersion, (IDictionary<string, TValue>)this); |
| } |
| |
| /// <summary> |
| /// Returns a copy of the current <see cref="CharArrayDictionary{TValue}"/> as a new instance of <see cref="CharArrayDictionary{TValue}"/> |
| /// using the specified <paramref name="matchVersion"/> value. Preserves the value of <c>ignoreCase</c> from the current instance. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <a href="#version">Version |
| /// note</a> above for details. </param> |
| /// <returns> A copy of the current <see cref="CharArrayDictionary{TValue}"/> as a <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| // LUCENENET specific - allow .NET-like syntax for copying CharArrayDictionary |
| public virtual CharArrayDictionary<TValue> ToCharArrayDictionary(LuceneVersion matchVersion) |
| { |
| return CharArrayDictionary.Copy(matchVersion, (IDictionary<string, TValue>)this); |
| } |
| |
| /// <summary> |
| /// Returns a copy of the current <see cref="CharArrayDictionary{TValue}"/> as a new instance of <see cref="CharArrayDictionary{TValue}"/> |
| /// using the specified <paramref name="matchVersion"/> and <paramref name="ignoreCase"/> values. |
| /// </summary> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <a href="#version">Version |
| /// note</a> above for details. </param> |
| /// <param name="ignoreCase"><c>false</c> if and only if the set should be case sensitive otherwise <c>true</c>.</param> |
| /// <returns> A copy of the current <see cref="CharArrayDictionary{TValue}"/> as a <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| // LUCENENET specific - allow .NET-like syntax for copying CharArrayDictionary |
| public virtual CharArrayDictionary<TValue> ToCharArrayDictionary(LuceneVersion matchVersion, bool ignoreCase) |
| { |
| return new CharArrayDictionary<TValue>(matchVersion, this, ignoreCase); |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get.</param> |
| /// <param name="startIndex">The position of the <paramref name="text"/> where the target text begins.</param> |
| /// <param name="length">The total length of the <paramref name="text"/>.</param> |
| /// <param name="value">When this method returns, contains the value associated with the specified text, |
| /// if the text 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="CharArrayDictionary{TValue}"/> contains an element with the specified text; otherwise, <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| public virtual bool TryGetValue(char[] text, int startIndex, int length, [MaybeNullWhen(returnValue: false)] out TValue value) |
| { |
| var val = values[GetSlot(text, startIndex, length)]; |
| if (val != null) |
| { |
| value = val.Value; |
| return true; |
| } |
| value = default; |
| return false; |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get.</param> |
| /// <param name="value">When this method returns, contains the value associated with the specified text, |
| /// if the text 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="CharArrayDictionary{TValue}"/> contains an element with the specified text; otherwise, <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool TryGetValue(char[] text, [MaybeNullWhen(returnValue: false)] out TValue value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| var val = values[GetSlot(text, 0, text.Length)]; |
| if (val != null) |
| { |
| value = val.Value; |
| return true; |
| } |
| value = default; |
| return false; |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get.</param> |
| /// <param name="value">When this method returns, contains the value associated with the specified text, |
| /// if the text 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="CharArrayDictionary{TValue}"/> contains an element with the specified text; otherwise, <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| public virtual bool TryGetValue(ICharSequence text, [MaybeNullWhen(returnValue: false)] out TValue value) |
| { |
| if (text is null || !text.HasValue) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (text is StringCharSequence strCs) |
| return TryGetValue(strCs.Value!, out value); |
| if (text is CharArrayCharSequence charArrayCs) |
| return TryGetValue(charArrayCs.Value!, out value); |
| if (text is StringBuilderCharSequence stringBuilderCs) // LUCENENET: Indexing into a StringBuilder is slow, so materialize |
| return TryGetValue(stringBuilderCs.Value!.ToString(), out value); |
| |
| var val = values[GetSlot(text)]; |
| if (val != null) |
| { |
| value = val.Value; |
| return true; |
| } |
| value = default; |
| return false; |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get.</param> |
| /// <param name="value">When this method returns, contains the value associated with the specified text, |
| /// if the text 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="CharArrayDictionary{TValue}"/> contains an element with the specified text; otherwise, <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool TryGetValue(string text, [NotNullWhen(returnValue: false)] out TValue value) |
| { |
| var val = values[GetSlot(text)]; |
| if (val != null) |
| { |
| value = val.Value!; |
| return true; |
| } |
| value = default!; |
| return false; |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get.</param> |
| /// <param name="value">When this method returns, contains the value associated with the specified text, |
| /// if the text 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="CharArrayDictionary{TValue}"/> contains an element with the specified text; otherwise, <c>false</c>.</returns> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual bool TryGetValue<T>(T text, [MaybeNullWhen(returnValue: false)] out TValue value) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| // 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 (text is string str) |
| return TryGetValue(str, out value); |
| if (text is char[] charArray) |
| return TryGetValue(charArray, 0, charArray.Length, out value); |
| if (text is ICharSequence cs) |
| return TryGetValue(cs, out value); |
| |
| var returnType = CharArrayDictionary.ConvertObjectToChars(text, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| return TryGetValue(s, out value); |
| else |
| return TryGetValue(chars, out value); |
| } |
| |
| /// <summary> |
| /// Gets or sets the value associated with the specified text. |
| /// <para/> |
| /// <b>Note:</b> If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// </summary> |
| /// <param name="text">The text of the value to get or set.</param> |
| /// <param name="startIndex">The position of the <paramref name="text"/> where the target text begins.</param> |
| /// <param name="length">The total length of the <paramref name="text"/>.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="length"/> is less than zero.</exception> |
| /// <exception cref="ArgumentException"><paramref name="startIndex"/> and <paramref name="length"/> refer to a position outside of <paramref name="text"/>.</exception> |
| public virtual TValue this[char[] text, int startIndex, int length] |
| { |
| get => Get(text, startIndex, length, throwIfNotFound: true); |
| set => Set(text, startIndex, length, value); |
| } |
| |
| /// <summary> |
| /// Gets or sets the value associated with the specified text. |
| /// <para/> |
| /// <b>Note:</b> If ignoreCase is <c>true</c> for this dictionary, the text array will be directly modified. |
| /// The user should never modify this text array after calling this setter. |
| /// </summary> |
| /// <param name="text">The text of the value to get or set.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual TValue this[char[] text] |
| { |
| get => Get(text, throwIfNotFound: true); |
| set => Set(text, value); |
| } |
| |
| /// <summary> |
| /// Gets or sets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get or set.</param> |
| /// <exception cref="ArgumentNullException"> |
| /// <paramref name="text"/> is <c>null</c>. |
| /// <para/> |
| /// -or- |
| /// <para/> |
| /// The <paramref name="text"/>'s <see cref="ICharSequence.HasValue"/> property returns <c>false</c>. |
| /// </exception> |
| public virtual TValue this[ICharSequence text] |
| { |
| get => Get(text, throwIfNotFound: true); |
| set => Set(text, value); |
| } |
| |
| /// <summary> |
| /// Gets or sets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get or set.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual TValue this[string text] |
| { |
| get => Get(text, throwIfNotFound: true); |
| set => Set(text, value); |
| } |
| |
| /// <summary> |
| /// Gets or sets the value associated with the specified text. |
| /// </summary> |
| /// <param name="text">The text of the value to get or set.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="text"/> is <c>null</c>.</exception> |
| public virtual TValue this[object text] |
| { |
| get => Get(text, throwIfNotFound: true); |
| set => Set(text, value); |
| } |
| |
| /// <summary> |
| /// Gets a collection containing the keys in the <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| public virtual CharArraySet Keys => KeySet; |
| |
| ICollection<string> IDictionary<string, TValue>.Keys => KeySet; |
| |
| IEnumerable<string> IReadOnlyDictionary<string, TValue>.Keys => KeySet; |
| |
| |
| private volatile ValueCollection? valueSet; |
| |
| /// <summary> |
| /// Gets a collection containing the values in the <see cref="CharArrayDictionary{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 ValueCollection Values |
| { |
| get |
| { |
| if (valueSet is null) |
| { |
| valueSet = new ValueCollection(this); |
| } |
| return valueSet; |
| } |
| } |
| |
| ICollection<TValue> IDictionary<string, TValue>.Values => Values; |
| |
| IEnumerable<TValue> IReadOnlyDictionary<string, TValue>.Values => Values; |
| |
| #region Nested Class: ValueCollection |
| |
| /// <summary> |
| /// Class that represents the values in the <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| // LUCENENET specific |
| [DebuggerDisplay("Count = {Count}, Values = {ToString()}")] |
| public sealed class ValueCollection : ICollection<TValue>, ICollection, IReadOnlyCollection<TValue> |
| { |
| private readonly CharArrayDictionary<TValue> dictionary; |
| |
| /// <summary> |
| /// Initializes a new instance of <see cref="ValueCollection"/> for the provided <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| /// <param name="dictionary">The dictionary to read the values from.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is <c>null</c>.</exception> |
| public ValueCollection(CharArrayDictionary<TValue> dictionary) |
| { |
| this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); |
| } |
| |
| /// <summary> |
| /// Gets the number of elements contained in the <see cref="ValueCollection"/>. |
| /// </summary> |
| /// <remarks> |
| /// Retrieving the value of this property is an O(1) operation. |
| /// </remarks> |
| public int Count => dictionary.Count; |
| |
| bool ICollection<TValue>.IsReadOnly => true; |
| |
| bool ICollection.IsSynchronized => false; |
| |
| object ICollection.SyncRoot => ((ICollection)dictionary).SyncRoot; |
| |
| void ICollection<TValue>.Add(TValue item) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ValueCollectionSet); |
| } |
| |
| void ICollection<TValue>.Clear() |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ValueCollectionSet); |
| } |
| |
| /// <summary> |
| /// Determines whether the set contains a specific element. |
| /// </summary> |
| /// <param name="item">The element to locate in the set.</param> |
| /// <returns><c>true</c> if the set contains item; otherwise, <c>false</c>.</returns> |
| [SuppressMessage("Style", "IDE0002:Name can be simplified", Justification = "This is a false warning.")] |
| public bool Contains(TValue item) |
| { |
| for (int i = 0; i < dictionary.values.Length; i++) |
| { |
| var value = dictionary.values[i]; |
| if (JCG.EqualityComparer<TValue>.Equals(value, item)) |
| return true; |
| } |
| return false; |
| } |
| |
| /// <summary> |
| /// Copies the <see cref="ValueCollection"/> elements to an existing one-dimensional |
| /// array, starting at the specified array index. |
| /// </summary> |
| /// <param name="array">The one-dimensional array that is the destination of the elements copied from |
| /// the <see cref="ValueCollection"/>. The array must have zero-based indexing.</param> |
| /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param> |
| /// <exception cref="ArgumentNullException"><paramref name="array"/> is <c>null</c>.</exception> |
| /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception> |
| /// <exception cref="ArgumentException">The number of elements in the source <see cref="ValueCollection"/> |
| /// is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination |
| /// <paramref name="array"/>.</exception> |
| /// <remarks> |
| /// The elements are copied to the array in the same order in which the enumerator iterates through the |
| /// <see cref="ValueCollection"/>. |
| /// <para/> |
| /// This method is an O(<c>n</c>) operation, where <c>n</c> is <see cref="Count"/>. |
| /// </remarks> |
| public void CopyTo(TValue[] array, int arrayIndex) |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (arrayIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (arrayIndex > array.Length || dictionary.Count > array.Length - arrayIndex) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| foreach (var entry in this) |
| array[arrayIndex++] = entry!; |
| } |
| |
| void ICollection.CopyTo(Array array, int index) |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (array.Rank != 1) |
| throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); |
| if (array.GetLowerBound(0) != 0) |
| throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); |
| if (index < 0) |
| throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (array.Length - index < dictionary.Count) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| if (array is TValue[] values) |
| { |
| CopyTo(values, index); |
| } |
| else |
| { |
| try |
| { |
| object?[] objects = (object?[])array; |
| foreach (var entry in this) |
| objects[index++] = entry; |
| } |
| catch (ArrayTypeMismatchException) |
| { |
| throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Returns an enumerator that iterates through the <see cref="ValueCollection"/>. |
| /// </summary> |
| /// <returns>An enumerator that iterates through the <see cref="ValueCollection"/>.</returns> |
| /// <remarks> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to |
| /// the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably |
| /// invalidated and the next call to <see cref="Enumerator.MoveNext()"/> or <see cref="IEnumerator.Reset()"/> |
| /// throws an <see cref="InvalidOperationException"/>. |
| /// <para/> |
| /// This method is an <c>O(log n)</c> operation. |
| /// </remarks> |
| public Enumerator GetEnumerator() |
| { |
| return new Enumerator(dictionary); |
| } |
| |
| IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => GetEnumerator(); |
| |
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
| |
| bool ICollection<TValue>.Remove(TValue item) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ValueCollectionSet); |
| } |
| |
| /// <summary> |
| /// Returns a string that represents the current collection. |
| /// <para/> |
| /// The presentation has a specific format. It is enclosed by square |
| /// brackets ("[]"). Elements are separated by ', ' (comma and space). |
| /// <c>null</c> values are represented as the string "null". |
| /// </summary> |
| /// <returns>A string that represents the current collection.</returns> |
| public override string ToString() |
| { |
| using var i = GetEnumerator(); |
| if (!i.HasNext) |
| return "[]"; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.Append('['); |
| while (i.MoveNext()) |
| { |
| TValue? value = i.Current; |
| if (sb.Length > 1) |
| { |
| sb.Append(',').Append(' '); |
| } |
| if (value is not null) |
| sb.Append(value.ToString()); |
| else |
| sb.Append("null"); |
| } |
| |
| return sb.Append(']').ToString(); |
| } |
| |
| #region Nested Struct: Enumerator |
| |
| /// <summary> |
| /// Enumerates the elements of a <see cref="ValueCollection"/>. |
| /// </summary> |
| /// <remarks> |
| /// The <c>foreach</c> statement of the C# language (<c>for each</c> in C++, <c>For Each</c> in Visual Basic) |
| /// hides the complexity of enumerators. Therefore, using <c>foreach</c> is recommended instead of directly manipulating the enumerator. |
| /// <para/> |
| /// Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. |
| /// <para/> |
| /// Initially, the enumerator is positioned before the first element in the collection. At this position, the |
| /// <see cref="Current"/> property is undefined. Therefore, you must call the |
| /// <see cref="MoveNext()"/> method to advance the enumerator to the first element |
| /// of the collection before reading the value of <see cref="Current"/>. |
| /// <para/> |
| /// The <see cref="Current"/> property returns the same object until |
| /// <see cref="MoveNext()"/> is called. <see cref="MoveNext()"/> |
| /// sets <see cref="Current"/> to the next element. |
| /// <para/> |
| /// If <see cref="MoveNext()"/> passes the end of the collection, the enumerator is |
| /// positioned after the last element in the collection and <see cref="MoveNext()"/> |
| /// returns <c>false</c>. When the enumerator is at this position, subsequent calls to <see cref="MoveNext()"/> |
| /// also return <c>false</c>. If the last call to <see cref="MoveNext()"/> returned <c>false</c>, |
| /// <see cref="Current"/> is undefined. You cannot set <see cref="Current"/> |
| /// to the first element of the collection again; you must create a new enumerator object instead. |
| /// <para/> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, |
| /// such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call |
| /// to <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> throws an |
| /// <see cref="InvalidOperationException"/>. |
| /// <para/> |
| /// The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is |
| /// intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the |
| /// collection during the entire enumeration. To allow the collection to be accessed by multiple threads for |
| /// reading and writing, you must implement your own synchronization. |
| /// </remarks> |
| // LUCENENET specific |
| public readonly struct Enumerator : IEnumerator<TValue>, IEnumerator |
| { |
| private readonly CharArrayDictionary<TValue>.Enumerator entryIterator; |
| |
| internal Enumerator(CharArrayDictionary<TValue> dictionary) // LUCENENET specific - marked internal to match .NET collections |
| { |
| this.entryIterator = dictionary.GetEnumerator(); |
| } |
| |
| /// <summary> |
| /// Gets the element at the current position of the enumerator. |
| /// </summary> |
| /// <remarks> |
| /// <see cref="Current"/> is undefined under any of the following conditions: |
| /// <list type="bullet"> |
| /// <item><description> |
| /// The enumerator is positioned before the first element of the collection. That happens after an |
| /// enumerator is created or after the <see cref="IEnumerator.Reset()"/> method is called. The <see cref="MoveNext()"/> |
| /// method must be called to advance the enumerator to the first element of the collection before reading the value of |
| /// the <see cref="Current"/> property. |
| /// </description></item> |
| /// <item><description> |
| /// The last call to <see cref="MoveNext()"/> returned <c>false</c>, which indicates the end of the collection and that the |
| /// enumerator is positioned after the last element of the collection. |
| /// </description></item> |
| /// <item><description> |
| /// The enumerator is invalidated due to changes made in the collection, such as adding, modifying, or deleting elements. |
| /// </description></item> |
| /// </list> |
| /// <para/> |
| /// <see cref="Current"/> does not move the position of the enumerator, and consecutive calls to <see cref="Current"/> return |
| /// the same object until either <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> is called. |
| /// </remarks> |
| public TValue Current => entryIterator.CurrentValue!; |
| |
| object IEnumerator.Current => entryIterator.CurrentValue!; |
| |
| /// <summary> |
| /// Releases all resources used by the <see cref="Enumerator"/>. |
| /// </summary> |
| public void Dispose() |
| { |
| entryIterator.Dispose(); |
| } |
| |
| /// <summary> |
| /// Advances the enumerator to the next element of the <see cref="ValueCollection"/>. |
| /// </summary> |
| /// <returns><c>true</c> if the enumerator was successfully advanced to the next element; |
| /// <c>false</c> if the enumerator has passed the end of the collection.</returns> |
| /// <exception cref="InvalidOperationException">The collection was modified after the enumerator was created.</exception> |
| /// <remarks> |
| /// After an enumerator is created, the enumerator is positioned before the first element in the collection, |
| /// and the first call to the <see cref="MoveNext()"/> method advances the enumerator to the first element |
| /// of the collection. |
| /// <para/> |
| /// If MoveNext passes the end of the collection, the enumerator is positioned after the last element in the |
| /// collection and <see cref="MoveNext()"/> returns <c>false</c>. When the enumerator is at this position, |
| /// subsequent calls to <see cref="MoveNext()"/> also return <c>false</c>. |
| /// <para/> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to the |
| /// collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated |
| /// and the next call to <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> throws an |
| /// <see cref="InvalidOperationException"/>. |
| /// </remarks> |
| public bool MoveNext() |
| { |
| return entryIterator.MoveNext(); |
| } |
| |
| private void Reset() |
| { |
| ((IEnumerator)entryIterator).Reset(); |
| } |
| |
| void IEnumerator.Reset() => Reset(); |
| |
| internal bool HasNext => entryIterator.HasNext; |
| } |
| |
| #endregion |
| } |
| |
| #endregion Nested Class: ValueCollection |
| |
| /// <summary> |
| /// <c>true</c> if the <see cref="CharArrayDictionary{TValue}"/> is read-only; otherwise <c>false</c>. |
| /// </summary> |
| public virtual bool IsReadOnly => false; |
| |
| #endregion For .NET Support LUCENENET |
| |
| /// <summary> |
| /// Returns an enumerator that iterates through the <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| /// <returns>A <see cref="CharArrayDictionary{TValue}.Enumerator"/> for the |
| /// <see cref="CharArrayDictionary{TValue}"/>.</returns> |
| /// <remarks> |
| /// For purposes of enumeration, each item is a <see cref="KeyValuePair{TKey, TValue}"/> structure |
| /// representing a value and its text. There are also properties allowing direct access |
| /// to the <see cref="T:char[]"/> array of each element and quick conversions to <see cref="string"/> or <see cref="ICharSequence"/>. |
| /// <para/> |
| /// The <c>foreach</c> statement of the C# language (<c>for each</c> in C++, <c>For Each</c> in Visual Basic) |
| /// hides the complexity of enumerators. Therefore, using <c>foreach</c> is recommended instead of directly manipulating the enumerator. |
| /// <para/> |
| /// This enumerator can be used to read the data in the collection, or modify the corresponding value at the current position. |
| /// <para/> |
| /// Initially, the enumerator is positioned before the first element in the collection. At this position, the |
| /// <see cref="Enumerator.Current"/> property is undefined. Therefore, you must call the |
| /// <see cref="Enumerator.MoveNext()"/> method to advance the enumerator to the first element |
| /// of the collection before reading the value of <see cref="Enumerator.Current"/>. |
| /// <para/> |
| /// The <see cref="Enumerator.Current"/> property returns the same object until |
| /// <see cref="Enumerator.MoveNext()"/> is called. <see cref="Enumerator.MoveNext()"/> |
| /// sets <see cref="Enumerator.Current"/> to the next element. |
| /// <para/> |
| /// If <see cref="Enumerator.MoveNext()"/> passes the end of the collection, the enumerator is |
| /// positioned after the last element in the collection and <see cref="Enumerator.MoveNext()"/> |
| /// returns <c>false</c>. When the enumerator is at this position, subsequent calls to <see cref="Enumerator.MoveNext()"/> |
| /// also return <c>false</c>. If the last call to <see cref="Enumerator.MoveNext()"/> returned <c>false</c>, |
| /// <see cref="Enumerator.Current"/> is undefined. You cannot set <see cref="Enumerator.Current"/> |
| /// to the first element of the collection again; you must create a new enumerator object instead. |
| /// <para/> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, |
| /// such as adding, modifying, or deleting elements (other than through the <see cref="Enumerator.SetValue(TValue)"/> method), |
| /// the enumerator is irrecoverably invalidated and the next call |
| /// to <see cref="Enumerator.MoveNext()"/> or <see cref="IEnumerator.Reset()"/> throws an |
| /// <see cref="InvalidOperationException"/>. |
| /// <para/> |
| /// The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is |
| /// intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the |
| /// collection during the entire enumeration. To allow the collection to be accessed by multiple threads for |
| /// reading and writing, you must implement your own synchronization. |
| /// <para/> |
| /// Default implementations of collections in the <see cref="J2N.Collections.Generic"/> namespace are not synchronized. |
| /// <para/> |
| /// This method is an O(1) operation. |
| /// </remarks> |
| public Enumerator GetEnumerator() |
| { |
| return new Enumerator(this, Enumerator.KeyValuePair); |
| } |
| |
| IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator() => GetEnumerator(); |
| |
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
| |
| ICharArrayDictionaryEnumerator ICharArrayDictionary.GetEnumerator() => GetEnumerator(); |
| |
| IDictionaryEnumerator IDictionary.GetEnumerator() |
| { |
| return new Enumerator(this, Enumerator.DictEntry); |
| } |
| |
| void IDictionary.Remove(object key) |
| { |
| throw UnsupportedOperationException.Create(); |
| } |
| |
| bool IDictionary<string, TValue>.Remove(string key) |
| { |
| throw UnsupportedOperationException.Create(); |
| } |
| |
| bool ICollection<KeyValuePair<string, TValue>>.Remove(KeyValuePair<string, TValue> item) |
| { |
| throw UnsupportedOperationException.Create(); |
| } |
| |
| [SuppressMessage("Style", "IDE0083:Use pattern matching", Justification = "Following Microsoft's coding style")] |
| void ICollection.CopyTo(Array array, int index) |
| { |
| if (array is null) |
| throw new ArgumentNullException(nameof(array)); |
| if (array.Rank != 1) |
| throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); |
| if (array.GetLowerBound(0) != 0) |
| throw new ArgumentException(SR.Arg_NonZeroLowerBound); |
| if (index < 0 || index > array.Length) |
| throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_NeedNonNegNum); |
| if (array.Length - index < Count) |
| throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); |
| |
| if (array is KeyValuePair<string, TValue>[] strings) |
| { |
| CopyTo(strings, index); |
| } |
| else if (array is KeyValuePair<char[], TValue>[] chars) |
| { |
| CopyTo(chars, index); |
| } |
| else if (array is KeyValuePair<ICharSequence, TValue>[] charSequences) |
| { |
| CopyTo(charSequences, index); |
| } |
| else if (array is DictionaryEntry[] dictEntryArray) |
| { |
| foreach (var item in this) |
| dictEntryArray[index++] = new DictionaryEntry(item.Key, item.Value); |
| } |
| else |
| { |
| if (array is not object[] objects) |
| { |
| throw new ArgumentException(SR.Argument_InvalidArrayType); |
| } |
| try |
| { |
| foreach (var item in this) |
| objects[index++] = item; |
| } |
| catch (ArrayTypeMismatchException) |
| { |
| throw new ArgumentException(SR.Argument_InvalidArrayType); |
| } |
| } |
| } |
| |
| bool IDictionary.IsFixedSize => false; |
| |
| bool IDictionary.IsReadOnly => IsReadOnly; |
| |
| ICollection IDictionary.Keys => Keys; |
| |
| ICollection IDictionary.Values => Values; |
| |
| bool ICollection.IsSynchronized => false; |
| |
| object ICollection.SyncRoot => this; |
| |
| object? IDictionary.this[object key] |
| { |
| get => Get(key, throwIfNotFound: false); |
| set |
| { |
| if (key is null) |
| throw new ArgumentNullException(nameof(key)); |
| |
| if (value is null && default(TValue) != null) |
| throw new ArgumentNullException(nameof(value)); |
| |
| TValue val; |
| try |
| { |
| val = (TValue)value!; |
| } |
| catch (InvalidCastException) |
| { |
| throw new ArgumentException(string.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); |
| } |
| var returnType = CharArrayDictionary.ConvertObjectToChars(key, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| { |
| Set(s, val); |
| } |
| else |
| { |
| Set(chars, val); |
| } |
| } |
| } |
| |
| void IDictionary.Add(object key, object? value) |
| { |
| if (key is null) |
| throw new ArgumentNullException(nameof(key)); |
| |
| TValue val; |
| try |
| { |
| val = (TValue)value!; |
| } |
| catch (InvalidCastException) |
| { |
| throw new ArgumentException(string.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); |
| } |
| var returnType = CharArrayDictionary.ConvertObjectToChars(key, out char[] chars, out string s); |
| if (returnType == CharArrayDictionary.CharReturnType.String) |
| { |
| Add(s, val); |
| } |
| else |
| { |
| Add(chars, val); |
| } |
| } |
| |
| bool IDictionary.Contains(object key) => ContainsKey(key); |
| |
| /// <summary> |
| /// Gets the number of text/value pairs contained in the <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| public virtual int Count => count; |
| |
| /// <summary> |
| /// Returns a string that represents the current object. (Inherited from <see cref="object"/>.) |
| /// </summary> |
| public override string ToString() |
| { |
| if (count == 0) |
| return "{}"; |
| |
| var sb = new StringBuilder("{"); |
| |
| using (var iter1 = this.GetEnumerator()) |
| { |
| while (iter1.MoveNext()) |
| { |
| if (sb.Length > 1) |
| { |
| sb.Append(", "); |
| } |
| sb.Append(iter1.CurrentKey); |
| sb.Append('='); |
| if (iter1.CurrentValue is not null) |
| sb.Append(iter1.CurrentValue); |
| else |
| sb.Append("null"); |
| } |
| } |
| |
| return sb.Append('}').ToString(); |
| } |
| |
| |
| |
| // LUCENENET: Removed entrySet because in .NET we use the collection itself as the IEnumerable |
| private CharArraySet? keySet = null; |
| |
| // LUCENENET: Removed entrySet(), createEntrySet() because in .NET we use the collection itself as the IEnumerable |
| |
| // LUCENENET: Removed originalKeySet() because we fixed infinite recursion |
| // by adding a custom enumerator for KeyCollection. |
| |
| /// <summary> |
| /// Returns an <see cref="CharArraySet"/> view on the dictionary's keys. |
| /// The set will use the same <see cref="matchVersion"/> as this dictionary. |
| /// </summary> |
| private CharArraySet KeySet |
| { |
| get |
| { |
| if (keySet is null) |
| { |
| // prevent adding of entries |
| keySet = new KeyCollection(this); |
| } |
| return keySet; |
| } |
| } |
| |
| #region Nested Class: KeyCollection |
| |
| // LUCENENET: This was an anonymous class in Java |
| [DebuggerDisplay("Count = {Count}, Values = {ToString()}")] |
| private sealed class KeyCollection : CharArraySet |
| { |
| internal KeyCollection(CharArrayDictionary<TValue> map) |
| : base(map) |
| { |
| } |
| |
| public override bool IsReadOnly => true; |
| |
| public override bool Add<T>(T text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_KeyCollectionSet); |
| } |
| public override bool Add(ICharSequence text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_KeyCollectionSet); |
| } |
| public override bool Add(string text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_KeyCollectionSet); |
| } |
| public override bool Add(char[] text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_KeyCollectionSet); |
| } |
| } |
| |
| #endregion Nested Class: KeyCollection |
| |
| #region Nested Class: Enumerator |
| |
| /// <summary> |
| /// Enumerates the elements of a <see cref="CharArrayDictionary{TValue}"/>. |
| /// <para/> |
| /// This enumerator exposes <see cref="CurrentKey"/> efficient access to the |
| /// underlying <see cref="T:char[]"/>. It also has <see cref="CurrentKeyString"/>, |
| /// <see cref="CurrentKeyCharSequence"/>, and <see cref="CurrentValue"/> properties for |
| /// convenience. |
| /// </summary> |
| /// <remarks> |
| /// The <c>foreach</c> statement of the C# language (<c>for each</c> in C++, <c>For Each</c> in Visual Basic) |
| /// hides the complexity of enumerators. Therefore, using <c>foreach</c> is recommended instead of directly manipulating the enumerator. |
| /// <para/> |
| /// This enumerator can be used to read the data in the collection, or modify the corresponding value at the current position. |
| /// <para/> |
| /// Initially, the enumerator is positioned before the first element in the collection. At this position, the |
| /// <see cref="Current"/> property is undefined. Therefore, you must call the |
| /// <see cref="MoveNext()"/> method to advance the enumerator to the first element |
| /// of the collection before reading the value of <see cref="Current"/>. |
| /// <para/> |
| /// The <see cref="Current"/> property returns the same object until |
| /// <see cref="MoveNext()"/> is called. <see cref="MoveNext()"/> |
| /// sets <see cref="Current"/> to the next element. |
| /// <para/> |
| /// If <see cref="MoveNext()"/> passes the end of the collection, the enumerator is |
| /// positioned after the last element in the collection and <see cref="MoveNext()"/> |
| /// returns <c>false</c>. When the enumerator is at this position, subsequent calls to <see cref="MoveNext()"/> |
| /// also return <c>false</c>. If the last call to <see cref="MoveNext()"/> returned <c>false</c>, |
| /// <see cref="Current"/> is undefined. You cannot set <see cref="Current"/> |
| /// to the first element of the collection again; you must create a new enumerator object instead. |
| /// <para/> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, |
| /// such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call |
| /// to <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> throws an |
| /// <see cref="InvalidOperationException"/>. |
| /// <para/> |
| /// The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is |
| /// intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the |
| /// collection during the entire enumeration. To allow the collection to be accessed by multiple threads for |
| /// reading and writing, you must implement your own synchronization. |
| /// </remarks> |
| // LUCENENET: An attempt was made to make this into a struct, but since it has mutable state that didn't work. So, this should remain a class. |
| public sealed class Enumerator : IEnumerator<KeyValuePair<string, TValue>>, IDictionaryEnumerator, ICharArrayDictionaryEnumerator |
| { |
| private readonly CharArrayDictionary<TValue> dictionary; |
| private readonly int getEnumeratorRetType; // What should Enumerator.Current return? |
| |
| internal const int KeyValuePair = 1; |
| internal const int DictEntry = 2; |
| |
| internal int pos = -1; |
| internal int lastPos; |
| internal readonly bool allowModify; |
| |
| private int version; // LUCENENET specific - track when the enumerator is broken by mutating the state of the original collection |
| private bool notStartedOrEnded; // LUCENENET specific |
| |
| internal Enumerator(CharArrayDictionary<TValue> dictionary, int getEnumeratorRetType) |
| { |
| this.dictionary = dictionary; |
| this.getEnumeratorRetType = getEnumeratorRetType; |
| this.version = dictionary.version; |
| this.allowModify = !dictionary.IsReadOnly; |
| this.notStartedOrEnded = true; |
| GoNext(); |
| } |
| |
| private void GoNext() |
| { |
| lastPos = pos; |
| pos++; |
| while (pos < dictionary.keys.Length && dictionary.keys[pos] is null) |
| { |
| pos++; |
| } |
| } |
| |
| internal bool HasNext => pos < dictionary.keys.Length; |
| |
| /// <summary> |
| /// Gets the current text as a <see cref="CharArrayCharSequence"/>. |
| /// </summary> |
| // LUCENENET specific - quick access to ICharSequence interface |
| public ICharSequence CurrentKeyCharSequence |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| return key.AsCharSequence(); |
| } |
| } |
| |
| /// <summary> |
| /// Gets the current text... do not modify the returned char[]. |
| /// </summary> |
| [SuppressMessage("Microsoft.Performance", "CA1819", Justification = "Lucene's design requires some writable array properties")] |
| [WritableArray] |
| public char[] CurrentKey |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| return key; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the current text as a newly created <see cref="string"/> object. |
| /// </summary> |
| public string CurrentKeyString |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| return new string(key); |
| } |
| } |
| |
| /// <summary> |
| /// Gets the value associated with the current text. |
| /// </summary> |
| [MaybeNull] |
| public TValue CurrentValue |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| |
| var val = dictionary.values[lastPos]; |
| return val != null ? val.Value : default; |
| } |
| } |
| |
| /// <summary> |
| /// Sets the value associated with the current text. |
| /// </summary> |
| /// <returns>Returns the value prior to the update.</returns> |
| [return: MaybeNull] |
| public TValue SetValue(TValue value) |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| if (!allowModify) |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| |
| MapValue current = dictionary.values[lastPos]; |
| if (current is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| |
| TValue? old = current.Value; |
| // LUCENENET specific - increment the versions of both this enumerator |
| // and the original collection so only this enumerator instance isn't broken. |
| dictionary.version++; |
| version++; |
| current.Value = value; |
| return old; |
| } |
| |
| // LUCENENET: Next() and Remove() methods eliminated here |
| |
| #region Added for better .NET support LUCENENET |
| |
| /// <summary> |
| /// Releases all resources used by the <see cref="Enumerator"/>. |
| /// </summary> |
| public void Dispose() |
| { |
| // nothing to do |
| } |
| |
| /// <summary> |
| /// Advances the enumerator to the next element of the <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| /// <returns><c>true</c> if the enumerator was successfully advanced to the next element; |
| /// <c>false</c> if the enumerator has passed the end of the collection.</returns> |
| /// <exception cref="InvalidOperationException">The collection was modified after the enumerator was created.</exception> |
| /// <remarks> |
| /// After an enumerator is created, the enumerator is positioned before the first element in the collection, |
| /// and the first call to the <see cref="MoveNext()"/> method advances the enumerator to the first element |
| /// of the collection. |
| /// <para/> |
| /// If <see cref="MoveNext()"/> passes the end of the collection, the enumerator is positioned after the last element in the |
| /// collection and <see cref="MoveNext()"/> returns <c>false</c>. When the enumerator is at this position, |
| /// subsequent calls to <see cref="MoveNext()"/> also return <c>false</c>. |
| /// <para/> |
| /// An enumerator remains valid as long as the collection remains unchanged. If changes are made to the |
| /// collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated |
| /// and the next call to <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> throws an |
| /// <see cref="InvalidOperationException"/>. |
| /// </remarks> |
| public bool MoveNext() |
| { |
| if (version != dictionary.version) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| |
| if (!HasNext) |
| { |
| notStartedOrEnded = true; |
| return false; |
| } |
| notStartedOrEnded = false; |
| GoNext(); |
| return true; |
| } |
| |
| private void Reset() |
| { |
| if (version != dictionary.version) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| |
| pos = -1; |
| notStartedOrEnded = true; |
| GoNext(); |
| } |
| |
| void IEnumerator.Reset() => Reset(); |
| |
| void ICharArrayDictionaryEnumerator.Reset() => Reset(); |
| |
| /// <summary> |
| /// Gets the element at the current position of the enumerator. |
| /// </summary> |
| /// <remarks> |
| /// <see cref="Current"/> is undefined under any of the following conditions: |
| /// <list type="bullet"> |
| /// <item><description> |
| /// The enumerator is positioned before the first element of the collection. That happens after an |
| /// enumerator is created or after the <see cref="IEnumerator.Reset()"/> method is called. The <see cref="MoveNext()"/> |
| /// method must be called to advance the enumerator to the first element of the collection before reading the value of |
| /// the <see cref="Current"/> property. |
| /// </description></item> |
| /// <item><description> |
| /// The last call to <see cref="MoveNext()"/> returned <c>false</c>, which indicates the end of the collection and that the |
| /// enumerator is positioned after the last element of the collection. |
| /// </description></item> |
| /// <item><description> |
| /// The enumerator is invalidated due to changes made in the collection, such as adding, modifying, or deleting elements. |
| /// </description></item> |
| /// </list> |
| /// <para/> |
| /// <see cref="Current"/> does not move the position of the enumerator, and consecutive calls to <see cref="Current"/> return |
| /// the same object until either <see cref="MoveNext()"/> or <see cref="IEnumerator.Reset()"/> is called. |
| /// </remarks> |
| public KeyValuePair<string, TValue> Current |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| MapValue value = dictionary.values[lastPos]; |
| return new KeyValuePair<string, TValue>(new string(key), value.Value!); |
| } |
| } |
| |
| object IEnumerator.Current |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| char[] key = dictionary.keys[lastPos]; |
| if (key is null) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); |
| MapValue value = dictionary.values[lastPos]; |
| |
| if (getEnumeratorRetType == DictEntry) |
| { |
| return new DictionaryEntry(new string(key), value.Value); |
| } |
| else |
| { |
| return new KeyValuePair<string, TValue>(new string(key), value.Value!); |
| } |
| } |
| } |
| |
| object IDictionaryEnumerator.Key |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| return Current.Key; |
| } |
| } |
| |
| object? IDictionaryEnumerator.Value |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| return Current.Value; |
| } |
| } |
| |
| DictionaryEntry IDictionaryEnumerator.Entry |
| { |
| get |
| { |
| if (notStartedOrEnded) |
| throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); |
| |
| return new DictionaryEntry(Current.Key, Current.Value); |
| } |
| } |
| |
| bool ICharArrayDictionaryEnumerator.NotStartedOrEnded => notStartedOrEnded; |
| |
| #endregion |
| } |
| |
| #endregion Nested Class: Enumerator |
| |
| // 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. |
| |
| // LUCENENET: EntrySet class removed because in .NET we get the entries by calling GetEnumerator() on the dictionary. |
| |
| // LUCENENET: Moved UnmodifiableMap static methods to CharArrayDictionary class |
| |
| // LUCENENET: Moved Copy static methods to CharArrayDictionary class |
| |
| // LUCENENET: Removed EmptyMap() - use Empty instead |
| |
| // LUCENENET: Moved UnmodifiableCharArraymap to CharArrayDictionary class |
| |
| // LUCENENET: Moved EmptyCharArrayDictionary to CharArrayDictionary class |
| } |
| |
| /// <summary> |
| /// LUCENENET specific interface used so <see cref="CharArraySet"/> |
| /// can hold a reference to <see cref="CharArrayDictionary{TValue}"/> without |
| /// knowing its generic closing type for TValue. |
| /// </summary> |
| internal interface ICharArrayDictionary |
| { |
| void Clear(); |
| bool ContainsKey(char[] text, int startIndex, int length); |
| bool ContainsKey(char[] text); |
| bool ContainsKey<T>(T text); |
| bool ContainsKey(string text); |
| bool ContainsKey(ICharSequence text); |
| int Count { get; } |
| bool IgnoreCase { get; } |
| bool IsReadOnly { get; } |
| LuceneVersion MatchVersion { get; } |
| bool Put(char[] text, int startIndex, int length); |
| bool Put(char[] text); |
| bool Put(ICharSequence text); |
| bool Put<T>(T text); |
| bool Put(string text); |
| void Set(char[] text, int startIndex, int length); |
| void Set(char[] text); |
| void Set(ICharSequence text); |
| void Set<T>(T text); |
| void Set(string text); |
| ICharArrayDictionaryEnumerator GetEnumerator(); |
| } |
| |
| /// <summary> |
| /// LUCENENET specific interface used so <see cref="CharArraySet"/> can |
| /// iterate the keys of <see cref="CharArrayDictionary{TValue}"/> without |
| /// knowing its generic closing type for TValue. |
| /// </summary> |
| internal interface ICharArrayDictionaryEnumerator : IDisposable |
| { |
| bool NotStartedOrEnded { get; } |
| bool MoveNext(); |
| ICharSequence CurrentKeyCharSequence { get; } |
| string CurrentKeyString { get; } |
| char[] CurrentKey { get; } |
| void Reset(); |
| } |
| |
| public static class CharArrayDictionary // LUCENENET specific: CA1052 Static holder types should be Static or NotInheritable |
| { |
| /// <summary> |
| /// Returns a copy of the given dictionary as a <see cref="CharArrayDictionary{TValue}"/>. If the given dictionary |
| /// is a <see cref="CharArrayDictionary{TValue}"/> the ignoreCase property will be preserved. |
| /// <para> |
| /// <b>Note:</b> If you intend to create a copy of another <see cref="CharArrayDictionary{TValue}"/> where |
| /// the <see cref="LuceneVersion"/> of the source dictionary differs from its copy |
| /// <see cref="CharArrayDictionary{TValue}.CharArrayDictionary(LuceneVersion, IDictionary{string, TValue}, bool)"/> should be used instead. |
| /// The <see cref="Copy{TValue}(LuceneVersion, IDictionary{string, TValue})"/> will preserve the <see cref="LuceneVersion"/> of the |
| /// source dictionary if it is an instance of <see cref="CharArrayDictionary{TValue}"/>. |
| /// </para> |
| /// </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 dictionary is a <see cref="CharArrayDictionary{TValue}"/>. </param> |
| /// <param name="dictionary"> |
| /// a dictionary to copy </param> |
| /// <returns> a copy of the given dictionary as a <see cref="CharArrayDictionary{TValue}"/>. If the given dictionary |
| /// is a <see cref="CharArrayDictionary{TValue}"/> the ignoreCase property as well as the |
| /// <paramref name="matchVersion"/> will be of the given dictionary will be preserved. </returns> |
| public static CharArrayDictionary<TValue> Copy<TValue>(LuceneVersion matchVersion, IDictionary<string, TValue> dictionary) |
| { |
| if (dictionary == CharArrayDictionary<TValue>.Empty) |
| { |
| return CharArrayDictionary<TValue>.Empty; |
| } |
| |
| if (dictionary is CharArrayDictionary<TValue> m) |
| { |
| // 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][]; |
| Arrays.Copy(m.keys, 0, keys, 0, keys.Length); |
| var values = new CharArrayDictionary<TValue>.MapValue[m.values.Length]; |
| Arrays.Copy(m.values, 0, values, 0, values.Length); |
| m = new CharArrayDictionary<TValue>(m) { keys = keys, values = values }; |
| return m; |
| } |
| return new CharArrayDictionary<TValue>(matchVersion, dictionary, false); |
| } |
| |
| /// <summary> |
| /// Used by <see cref="CharArraySet"/> to copy <see cref="CharArrayDictionary{TValue}"/> without knowing |
| /// its generic type. |
| /// </summary> |
| internal static CharArrayDictionary<TValue> Copy<TValue>(LuceneVersion matchVersion, [DisallowNull] ICharArrayDictionary map) |
| { |
| return Copy(matchVersion, (IDictionary<string, TValue>)map); |
| } |
| |
| /// <summary> |
| /// Returns an unmodifiable <see cref="CharArrayDictionary{TValue}"/>. This allows to provide |
| /// unmodifiable views of internal dictionary for "read-only" use. |
| /// </summary> |
| /// <param name="map"> |
| /// a dictionary for which the unmodifiable dictionary is returned. </param> |
| /// <returns> an new unmodifiable <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| /// <exception cref="ArgumentException"> |
| /// if the given dictionary is <c>null</c>. </exception> |
| [Obsolete("Use the CharArrayDictionary<TValue>.AsReadOnly() instance method instead. This method will be removed in 4.8.0 release candidate."), EditorBrowsable(EditorBrowsableState.Never)] |
| public static CharArrayDictionary<TValue> UnmodifiableMap<TValue>(CharArrayDictionary<TValue> map) |
| { |
| if (map is null) |
| { |
| throw new ArgumentNullException(nameof(map)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| } |
| if (map == CharArrayDictionary<TValue>.Empty || map.Count == 0) |
| { |
| return CharArrayDictionary<TValue>.Empty; |
| } |
| if (map is CharArrayDictionary.ReadOnlyCharArrayDictionary<TValue>) |
| { |
| return map; |
| } |
| return new CharArrayDictionary.ReadOnlyCharArrayDictionary<TValue>(map); |
| } |
| |
| /// <summary> |
| /// Used by <see cref="CharArraySet"/> to create an <see cref="ReadOnlyCharArrayDictionary{TValue}"/> instance |
| /// without knowing the type of <typeparamref name="TValue"/>. |
| /// </summary> |
| internal static ICharArrayDictionary UnmodifiableMap<TValue>(ICharArrayDictionary map) |
| { |
| if (map is null) |
| { |
| throw new ArgumentNullException(nameof(map)); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) |
| } |
| if (map == CharArrayDictionary<TValue>.Empty || map.Count == 0) |
| { |
| return CharArrayDictionary<TValue>.Empty; |
| } |
| if (map is CharArrayDictionary.ReadOnlyCharArrayDictionary<TValue>) |
| { |
| return map; |
| } |
| return new CharArrayDictionary.ReadOnlyCharArrayDictionary<TValue>(map); |
| } |
| |
| #region Nested Class: ReadOnlyCharArrayDictionary<TValue> |
| |
| // package private CharArraySet instanceof check in CharArraySet |
| internal class ReadOnlyCharArrayDictionary<TValue> : CharArrayDictionary<TValue> |
| { |
| public ReadOnlyCharArrayDictionary([DisallowNull] CharArrayDictionary<TValue> map) |
| : base(map) |
| { } |
| |
| public ReadOnlyCharArrayDictionary([DisallowNull] ICharArrayDictionary map) |
| : base((CharArrayDictionary<TValue>)map) |
| { } |
| |
| private protected override CharArrayDictionary<TValue> AsReadOnlyImpl() => this; |
| |
| public override void Clear() |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override bool Put(char[] text, int startIndex, int length, TValue value, [MaybeNullWhen(true)] out TValue previousValue) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override bool Put(char[] text, TValue value, [MaybeNullWhen(true)] out TValue previousValue) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override bool Put(ICharSequence text, TValue value, [MaybeNullWhen(true)] out TValue previousValue) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override bool Put(string text, TValue value, [MaybeNullWhen(true)] out TValue previousValue) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override bool Put<T>(T text, TValue value, [MaybeNullWhen(true)] out TValue previousValue) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override bool Put(char[] text, int startIndex, int length) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override bool Put(char[] text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override bool Put(ICharSequence text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override bool Put(string text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override bool Put<T>(T text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| // LUCENENET: Removed CreateEntrySet() method - we use IsReadOnly to control whether it can be written to |
| |
| #region Added for better .NET support LUCENENET |
| |
| public override bool IsReadOnly => true; |
| |
| public override void Add(string text, TValue value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void Add(char[] text, TValue value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void Add(ICharSequence text, TValue value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void Add<T>(T text, TValue value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override TValue this[char[] text, int startIndex, int length] |
| { |
| get => base[text, startIndex, length]; |
| set => throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override TValue this[char[] text] |
| { |
| get => base[text]; |
| set => throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override TValue this[ICharSequence text] |
| { |
| get => base[text]; |
| set => throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override TValue this[string text] |
| { |
| get => base[text]; |
| set => throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override TValue this[object text] |
| { |
| get => base[text]; |
| set => throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IDictionary<string, TValue> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IDictionary<char[], TValue> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IDictionary<ICharSequence, TValue> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll<T>(IDictionary<T, TValue> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IEnumerable<KeyValuePair<string, TValue>> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IEnumerable<KeyValuePair<char[], TValue>> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll(IEnumerable<KeyValuePair<ICharSequence, TValue>> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| public override void PutAll<T>(IEnumerable<KeyValuePair<T, TValue>> collection) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(char[] text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(char[] text, TValue? value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(char[] text, int startIndex, int length) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(char[] text, int startIndex, int length, TValue? value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(ICharSequence text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(ICharSequence text, TValue? value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(string text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set(string text, TValue? value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set<T>(T text) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| internal override void Set<T>(T text, TValue? value) |
| { |
| throw UnsupportedOperationException.Create(SR.NotSupported_ReadOnlyCollection); |
| } |
| |
| #endregion |
| } |
| |
| #endregion |
| |
| #region Nested Class: EmptyCharArrayDictionary<V> |
| |
| /// <summary> |
| /// Empty <see cref="ReadOnlyCharArrayDictionary{V}"/> optimized for speed. |
| /// Contains checks will always return <c>false</c> or throw |
| /// NPE if necessary. |
| /// </summary> |
| internal class EmptyCharArrayDictionary<V> : ReadOnlyCharArrayDictionary<V> |
| { |
| [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "Clearly a bug with code analysis - we need the suppression to stop the obsolete warning")] |
| public EmptyCharArrayDictionary() |
| #pragma warning disable CS0618 // Type or member is obsolete |
| : base(new CharArrayDictionary<V>(LuceneVersion.LUCENE_CURRENT, 0, false)) |
| #pragma warning restore CS0618 // Type or member is obsolete |
| { |
| } |
| |
| public override bool ContainsKey(char[] text, int startIndex, int length) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| return false; |
| } |
| |
| public override bool ContainsKey(char[] text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| return false; |
| } |
| |
| public override bool ContainsKey(ICharSequence text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| return false; |
| } |
| |
| public override bool ContainsKey<T>(T text) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| return false; |
| } |
| |
| internal override V Get(char[] text, int startIndex, int length, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, new string(text, startIndex, length))); |
| return default!; |
| } |
| |
| internal override V Get(char[] text, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, new string(text))); |
| return default!; |
| } |
| |
| internal override V Get(ICharSequence text, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, text)); |
| return default!; |
| } |
| |
| internal override V Get<T>(T text, bool throwIfNotFound = true) |
| { |
| if (text is null) |
| throw new ArgumentNullException(nameof(text)); |
| |
| if (throwIfNotFound) |
| throw new KeyNotFoundException(string.Format(SR.Arg_KeyNotFoundWithKey, text)); |
| return default!; |
| } |
| } |
| |
| #endregion |
| |
| private readonly static CultureInfo invariant = CultureInfo.InvariantCulture; |
| private const string TrueString = "true"; |
| private const string FalseString = "false"; |
| |
| |
| internal enum CharReturnType |
| { |
| String = 0, |
| CharArray = 1, |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal static CharReturnType ConvertObjectToChars<T>(T key, out char[] chars, out string str) |
| { |
| #if FEATURE_SPANFORMATTABLE |
| Span<char> buffer = stackalloc char[256]; |
| #else |
| Span<char> buffer = stackalloc char[1]; |
| #endif |
| return ConvertObjectToChars(key, out chars, out str, buffer); |
| } |
| |
| |
| // LUCENENET: We need value types to be represented using the invariant |
| // culture, so it is consistent regardless of the current culture. |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| internal static CharReturnType ConvertObjectToChars<T>(T key, out char[] chars, out string str, Span<char> reuse) |
| { |
| chars = Array.Empty<char>(); |
| str = string.Empty; |
| |
| if (key is null) |
| { |
| return CharReturnType.CharArray; |
| } |
| |
| // Handle special cases |
| if (key is string strResult) |
| { |
| str = strResult; |
| return CharReturnType.String; |
| } |
| else if (key is char[] charArray) |
| { |
| chars = charArray; |
| return CharReturnType.CharArray; |
| } |
| else if (key is IList<char> charList) |
| { |
| char[] result = new char[charList.Count]; |
| charList.CopyTo(result, arrayIndex: 0); |
| chars = result; |
| return CharReturnType.CharArray; |
| } |
| else if (key is StringBuilder stringBuilder) |
| { |
| char[] result = new char[stringBuilder.Length]; |
| stringBuilder.CopyTo(sourceIndex: 0, result, destinationIndex: 0, stringBuilder.Length); |
| chars = result; |
| return CharReturnType.CharArray; |
| } |
| |
| // ICharSequence types |
| else if (key is StringCharSequence strCs) |
| { |
| str = strCs.Value ?? string.Empty; |
| return CharReturnType.String; |
| } |
| else if (key is CharArrayCharSequence charArrayCs) |
| { |
| chars = charArrayCs.Value ?? Array.Empty<char>(); |
| return CharReturnType.CharArray; |
| } |
| else if (key is StringBuilderCharSequence stringBuilderCs && stringBuilderCs.HasValue) |
| { |
| var sb = stringBuilderCs.Value!; |
| char[] result = new char[sb.Length]; |
| sb.CopyTo(sourceIndex: 0, result, destinationIndex: 0, sb.Length); |
| chars = result; |
| return CharReturnType.CharArray; |
| } |
| else if (key is ICharSequence cs && cs.HasValue) |
| { |
| int length = cs.Length; |
| char[] result = new char[length]; |
| for (int i = 0; i < length; i++) |
| { |
| result[i] = cs[i]; |
| } |
| chars = result; |
| return CharReturnType.CharArray; |
| } |
| |
| // These must be done prior to checking ISpanFormattable and IFormattable |
| else if (key is bool b) |
| { |
| str = b ? TrueString : FalseString; |
| return CharReturnType.String; |
| } |
| else if (key is double d) |
| { |
| str = J2N.Numerics.Double.ToString(d, invariant); |
| return CharReturnType.String; |
| } |
| else if (key is float f) |
| { |
| str = J2N.Numerics.Single.ToString(f, invariant); |
| return CharReturnType.String; |
| } |
| else if (key is J2N.Numerics.Number number) |
| { |
| str = number.ToString(invariant); |
| return CharReturnType.String; |
| } |
| |
| #if FEATURE_SPANFORMATTABLE |
| else if (key is ISpanFormattable spanFormattable && |
| spanFormattable.TryFormat(reuse, out int charsWritten, string.Empty.AsSpan(), invariant)) |
| { |
| chars = reuse.Slice(0, charsWritten).ToArray(); |
| return CharReturnType.CharArray; |
| } |
| #endif |
| else if (key is IFormattable formattable) |
| { |
| str = formattable.ToString(string.Empty, invariant); |
| return CharReturnType.String; |
| } |
| |
| using var context = new CultureContext(invariant); |
| str = key.ToString() ?? string.Empty; |
| return CharReturnType.String; |
| } |
| } |
| |
| /// <summary> |
| /// Extensions to <see cref="IDictionary{TKey, TValue}"/> for <see cref="CharArrayDictionary{TValue}"/>. |
| /// </summary> |
| // LUCENENET specific - allow .NET-like syntax for copying CharArrayDictionary |
| public static class DictionaryExtensions |
| { |
| /// <summary> |
| /// Returns a copy of the current <see cref="IDictionary{TKey, TValue}"/> as a <see cref="CharArrayDictionary{TValue}"/> |
| /// using the specified <paramref name="matchVersion"/> value. |
| /// </summary> |
| /// <typeparam name="TValue">The type of dictionary value.</typeparam> |
| /// <param name="dictionary"> |
| /// A <see cref="IDictionary{TKey, TValue}"/> to copy. </param> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <a href="#version">Version |
| /// note</a> above for details. </param> |
| /// <returns> A copy of the current dictionary as a <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is <c>null</c>.</exception> |
| public static CharArrayDictionary<TValue> ToCharArrayDictionary<TValue>(this IDictionary<string, TValue> dictionary, LuceneVersion matchVersion) |
| { |
| if (dictionary is null) |
| throw new ArgumentNullException(nameof(dictionary)); |
| |
| return CharArrayDictionary.Copy<TValue>(matchVersion, dictionary); |
| } |
| |
| /// <summary> |
| /// Returns a copy of the current <see cref="IDictionary{TKey, TValue}"/> as a <see cref="CharArrayDictionary{TValue}"/> |
| /// using the specified <paramref name="matchVersion"/> and <paramref name="ignoreCase"/> values. |
| /// </summary> |
| /// <typeparam name="TValue">The type of dictionary value.</typeparam> |
| /// <param name="dictionary"> |
| /// A <see cref="IDictionary{TKey, TValue}"/> to copy. </param> |
| /// <param name="matchVersion"> |
| /// compatibility match version see <a href="#version">Version |
| /// note</a> above for details. </param> |
| /// <param name="ignoreCase"><c>false</c> if and only if the set should be case sensitive otherwise <c>true</c>.</param> |
| /// <returns> A copy of the current dictionary as a <see cref="CharArrayDictionary{TValue}"/>. </returns> |
| /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is <c>null</c>.</exception> |
| public static CharArrayDictionary<TValue> ToCharArrayDictionary<TValue>(this IDictionary<string, TValue> dictionary, LuceneVersion matchVersion, bool ignoreCase) |
| { |
| if (dictionary is null) |
| throw new ArgumentNullException(nameof(dictionary)); |
| |
| return new CharArrayDictionary<TValue>(matchVersion, dictionary.Count, ignoreCase); |
| } |
| } |
| |
| /// <summary> |
| /// LUCENENET specific. Just a class to make error messages easier to manage in one place. |
| /// Ideally, these would be in resources so they can be localized (eventually), but at least |
| /// this half-measure will make that somewhat easier to do and is guaranteed not to cause |
| /// performance issues. |
| /// </summary> |
| internal static class SR |
| { |
| public const string Arg_ArrayPlusOffTooSmall = "Destination array is not long enough to copy all the items in the collection. Check array index and length."; |
| public const string Arg_KeyNotFoundWithKey = "The given text '{0}' was not present in the dictionary."; |
| public const string Arg_NonZeroLowerBound = "The lower bound of target array must be zero."; |
| public const string Arg_RankMultiDimNotSupported = "Only single dimensional arrays are supported for the requested action."; |
| public const string Arg_WrongType = "The value '{0}' is not of type '{1}' and cannot be used in this generic collection."; |
| |
| public const string Argument_AddingDuplicate = "An item with the same text has already been added. Key: {0}"; |
| public const string Argument_InvalidArrayType = "Target array type is not compatible with the type of items in the collection."; |
| |
| public const string ArgumentOutOfRange_IndexLength = "Index and length must refer to a location within the string."; |
| public const string ArgumentOutOfRange_NeedNonNegNum = "Non-negative number required."; |
| |
| public const string InvalidOperation_EnumFailedVersion = "Collection was modified after the enumerator was instantiated."; |
| public const string InvalidOperation_EnumOpCantHappen = "Enumeration has either not started or has already finished."; |
| |
| public const string NotSupported_KeyCollectionSet = "Mutating a text collection derived from a dictionary is not allowed."; |
| public const string NotSupported_ReadOnlyCollection = "Collection is read-only."; |
| public const string NotSupported_ValueCollectionSet = "Mutating a value collection derived from a dictionary is not allowed."; |
| } |
| } |