blob: a5a4d4c5fc25280fd739d7a107508e98ac67a004 [file] [log] [blame]
using J2N;
using J2N.Collections.Generic.Extensions;
using J2N.Collections.ObjectModel;
using J2N.Globalization;
using Lucene.Net.Diagnostics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using JCG = J2N.Collections.Generic;
namespace Lucene.Net.Support
{
/*
* 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.
*/
internal static class Collections
{
private static class EmptyListHolder<T>
{
public static readonly ReadOnlyList<T> EMPTY_LIST = new JCG.List<T>().AsReadOnly();
}
private static class EmptyDictionaryHolder<TKey, TValue>
{
public static readonly ReadOnlyDictionary<TKey, TValue> EMPTY_DICTIONARY = new JCG.Dictionary<TKey, TValue>().AsReadOnly(); // LUCENENET-615: Must support nullable keys
}
private static class EmptySetHolder<T>
{
public static readonly ReadOnlySet<T> EMPTY_SET = new JCG.HashSet<T>().AsReadOnly();
}
public static ReadOnlyList<T> EmptyList<T>()
{
return EmptyListHolder<T>.EMPTY_LIST; // LUCENENET NOTE: Enumerable.Empty<T>() fails to cast to IList<T> on .NET Core 3.x, so we just create a new list
}
public static ReadOnlyDictionary<TKey, TValue> EmptyMap<TKey, TValue>()
{
return EmptyDictionaryHolder<TKey, TValue>.EMPTY_DICTIONARY;
}
public static ReadOnlySet<T> EmptySet<T>()
{
return EmptySetHolder<T>.EMPTY_SET;
}
public static void Reverse<T>(IList<T> list)
{
int size = list.Count;
for (int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j--)
{
list.Swap(i, j);
}
}
public static IComparer<T> ReverseOrder<T>()
{
return (IComparer<T>)ReverseComparer<T>.REVERSE_ORDER;
}
public static IComparer<T> ReverseOrder<T>(IComparer<T> cmp)
{
if (cmp == null)
return ReverseOrder<T>();
if (cmp is ReverseComparer2<T>)
return ((ReverseComparer2<T>)cmp).cmp;
return new ReverseComparer2<T>(cmp);
}
public static IDictionary<TKey, TValue> SingletonMap<TKey, TValue>(TKey key, TValue value)
{
return new Dictionary<TKey, TValue> { { key, value } }.AsReadOnly();
}
/// <summary>
/// This is the same implementation of ToString from Java's AbstractCollection
/// (the default implementation for all sets and lists)
/// </summary>
public static string ToString<T>(ICollection<T> collection)
{
if (collection == null)
return "null";
if (collection.Count == 0)
{
return "[]";
}
bool isValueType = typeof(T).IsValueType;
using (var it = collection.GetEnumerator())
{
StringBuilder sb = new StringBuilder();
sb.Append('[');
it.MoveNext();
while (true)
{
T e = it.Current;
sb.Append(object.ReferenceEquals(e, collection) ? "(this Collection)" : (isValueType ? e.ToString() : ToString(e)));
if (!it.MoveNext())
{
return sb.Append(']').ToString();
}
sb.Append(',').Append(' ');
}
}
}
/// <summary>
/// This is the same implementation of ToString from Java's AbstractCollection
/// (the default implementation for all sets and lists), plus the ability
/// to specify culture for formatting of nested numbers and dates. Note that
/// this overload will change the culture of the current thread.
/// </summary>
public static string ToString<T>(ICollection<T> collection, CultureInfo culture)
{
using (var context = new CultureContext(culture))
{
return ToString(collection);
}
}
/// <summary>
/// This is the same implementation of ToString from Java's AbstractMap
/// (the default implementation for all dictionaries)
/// </summary>
public static string ToString<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
return "null";
if (dictionary.Count == 0)
{
return "{}";
}
bool keyIsValueType = typeof(TKey).IsValueType;
bool valueIsValueType = typeof(TValue).IsValueType;
using (var i = dictionary.GetEnumerator())
{
StringBuilder sb = new StringBuilder();
sb.Append('{');
i.MoveNext();
while (true)
{
KeyValuePair<TKey, TValue> e = i.Current;
TKey key = e.Key;
TValue value = e.Value;
sb.Append(object.ReferenceEquals(key, dictionary) ? "(this Dictionary)" : (keyIsValueType ? key.ToString() : ToString(key)));
sb.Append('=');
sb.Append(object.ReferenceEquals(value, dictionary) ? "(this Dictionary)" : (valueIsValueType ? value.ToString() : ToString(value)));
if (!i.MoveNext())
{
return sb.Append('}').ToString();
}
sb.Append(',').Append(' ');
}
}
}
/// <summary>
/// This is the same implementation of ToString from Java's AbstractMap
/// (the default implementation for all dictionaries), plus the ability
/// to specify culture for formatting of nested numbers and dates. Note that
/// this overload will change the culture of the current thread.
/// </summary>
public static string ToString<TKey, TValue>(IDictionary<TKey, TValue> dictionary, CultureInfo culture)
{
using (var context = new CultureContext(culture))
{
return ToString(dictionary);
}
}
/// <summary>
/// This is a helper method that assists with recursively building
/// a string of the current collection and all nested collections.
/// </summary>
public static string ToString(object obj)
{
Type t = obj.GetType();
if (t.IsGenericType
&& (t.ImplementsGenericInterface(typeof(ICollection<>)))
|| t.ImplementsGenericInterface(typeof(IDictionary<,>)))
{
dynamic genericType = Convert.ChangeType(obj, t);
return ToString(genericType);
}
return obj.ToString();
}
/// <summary>
/// This is a helper method that assists with recursively building
/// a string of the current collection and all nested collections, plus the ability
/// to specify culture for formatting of nested numbers and dates. Note that
/// this overload will change the culture of the current thread.
/// </summary>
public static string ToString(object obj, CultureInfo culture)
{
using (var context = new CultureContext(culture))
{
return ToString(obj);
}
}
#region Nested Types
#region ReverseComparer
//private class ReverseComparer : IComparer<IComparable>
//{
// internal static readonly ReverseComparer REVERSE_ORDER = new ReverseComparer();
// public int Compare(IComparable c1, IComparable c2)
// {
// return c2.CompareTo(c1);
// }
//}
// LUCENENET NOTE: When consolidating this, it turns out that only the
// CaseInsensitiveComparer works correctly in .NET (not sure why).
// So, this hybrid was made from the original Java implementation and the
// original implemenation (above) that used CaseInsensitiveComparer.
private class ReverseComparer<T> : IComparer<T>
{
internal static readonly ReverseComparer<T> REVERSE_ORDER = new ReverseComparer<T>();
public int Compare(T x, T y)
{
return CaseInsensitiveComparer.Default.Compare(y, x);
}
}
#endregion ReverseComparer
#region ReverseComparer2
private class ReverseComparer2<T> : IComparer<T>
{
/**
* The comparer specified in the static factory. This will never
* be null, as the static factory returns a ReverseComparer
* instance if its argument is null.
*
* @serial
*/
internal readonly IComparer<T> cmp;
public ReverseComparer2(IComparer<T> cmp)
{
if (Debugging.AssertsEnabled) Debugging.Assert(cmp != null);
this.cmp = cmp;
}
public int Compare(T t1, T t2)
{
return cmp.Compare(t2, t1);
}
public override bool Equals(object o)
{
return (o == this) ||
(o is ReverseComparer2<T> &&
cmp.Equals(((ReverseComparer2<T>)o).cmp));
}
public override int GetHashCode()
{
return cmp.GetHashCode() ^ int.MinValue;
}
public IComparer<T> Reversed()
{
return cmp;
}
}
#endregion ReverseComparer2
#endregion Nested Types
}
}