-
Notifications
You must be signed in to change notification settings - Fork 639
/
Collections.cs
280 lines (240 loc) · 9.71 KB
/
Collections.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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 is null)
return ReverseOrder<T>();
if (cmp is ReverseComparer2<T> reverseComparer2)
return reverseComparer2.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 is 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 is 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<T> : IComparer<T>
{
internal static readonly ReverseComparer<T> REVERSE_ORDER = new ReverseComparer<T>();
public int Compare(T x, T y)
{
// LUCENENET specific: Use J2N's Comparer<T> to mimic Java comparison behavior
return JCG.Comparer<T>.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> reverseComparer2 &&
cmp.Equals(reverseComparer2.cmp));
}
public override int GetHashCode()
{
return cmp.GetHashCode() ^ int.MinValue;
}
public IComparer<T> Reversed()
{
return cmp;
}
}
#endregion ReverseComparer2
#endregion Nested Types
}
}