mirror of https://github.com/Squidex/squidex.git
14 changed files with 183 additions and 262 deletions
@ -1,213 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
using System.Linq; |
|
||||
|
|
||||
#pragma warning disable IDE0044 // Add readonly modifier
|
|
||||
#pragma warning disable RECS0108 // Warns about static fields in generic types
|
|
||||
|
|
||||
namespace Squidex.Infrastructure.Collections |
|
||||
{ |
|
||||
public class ArrayDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TKey : notnull |
|
||||
{ |
|
||||
private static readonly KeyValuePair<TKey, TValue>[] EmptyItems = Array.Empty<KeyValuePair<TKey, TValue>>(); |
|
||||
private readonly IEqualityComparer<TKey> keyComparer; |
|
||||
private KeyValuePair<TKey, TValue>[] items; |
|
||||
|
|
||||
public TValue this[TKey key] |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (!TryGetValue(key, out var value)) |
|
||||
{ |
|
||||
throw new KeyNotFoundException(); |
|
||||
} |
|
||||
|
|
||||
return value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public IEnumerable<TKey> Keys |
|
||||
{ |
|
||||
get { return items.Select(x => x.Key); } |
|
||||
} |
|
||||
|
|
||||
public IEnumerable<TValue> Values |
|
||||
{ |
|
||||
get { return items.Select(x => x.Value); } |
|
||||
} |
|
||||
|
|
||||
public int Count |
|
||||
{ |
|
||||
get { return items.Length; } |
|
||||
} |
|
||||
|
|
||||
public ArrayDictionary() |
|
||||
: this(EqualityComparer<TKey>.Default, EmptyItems) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public ArrayDictionary(KeyValuePair<TKey, TValue>[] items) |
|
||||
: this(EqualityComparer<TKey>.Default, items) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public ArrayDictionary(IEqualityComparer<TKey> keyComparer, KeyValuePair<TKey, TValue>[] items) |
|
||||
{ |
|
||||
Guard.NotNull(items); |
|
||||
Guard.NotNull(keyComparer); |
|
||||
|
|
||||
this.items = items; |
|
||||
|
|
||||
this.keyComparer = keyComparer; |
|
||||
} |
|
||||
|
|
||||
public bool IsUnchanged(KeyValuePair<TKey, TValue>[] values) |
|
||||
{ |
|
||||
return ReferenceEquals(values, items); |
|
||||
} |
|
||||
|
|
||||
public ArrayDictionary<TKey, TValue> With(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) |
|
||||
{ |
|
||||
return With<ArrayDictionary<TKey, TValue>>(key, value, valueComparer); |
|
||||
} |
|
||||
|
|
||||
public TArray With<TArray>(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) where TArray : ArrayDictionary<TKey, TValue> |
|
||||
{ |
|
||||
var index = IndexOf(key); |
|
||||
|
|
||||
if (index < 0) |
|
||||
{ |
|
||||
var result = new KeyValuePair<TKey, TValue>[items.Length + 1]; |
|
||||
|
|
||||
Array.Copy(items, 0, result, 0, items.Length); |
|
||||
|
|
||||
result[^1] = new KeyValuePair<TKey, TValue>(key, value); |
|
||||
|
|
||||
return Create<TArray>(result); |
|
||||
} |
|
||||
|
|
||||
var existing = items[index].Value; |
|
||||
|
|
||||
if (valueComparer == null || !valueComparer.Equals(value, existing)) |
|
||||
{ |
|
||||
var result = new KeyValuePair<TKey, TValue>[items.Length]; |
|
||||
|
|
||||
Array.Copy(items, 0, result, 0, items.Length); |
|
||||
|
|
||||
result[index] = new KeyValuePair<TKey, TValue>(key, value); |
|
||||
|
|
||||
return Create<TArray>(result); |
|
||||
} |
|
||||
|
|
||||
return Self<TArray>(); |
|
||||
} |
|
||||
|
|
||||
public ArrayDictionary<TKey, TValue> Without(TKey key) |
|
||||
{ |
|
||||
return Without<ArrayDictionary<TKey, TValue>>(key); |
|
||||
} |
|
||||
|
|
||||
public TArray Without<TArray>(TKey key) where TArray : ArrayDictionary<TKey, TValue> |
|
||||
{ |
|
||||
var index = IndexOf(key); |
|
||||
|
|
||||
if (index < 0) |
|
||||
{ |
|
||||
return Self<TArray>(); |
|
||||
} |
|
||||
|
|
||||
if (Count == 1) |
|
||||
{ |
|
||||
return Create<TArray>(EmptyItems); |
|
||||
} |
|
||||
|
|
||||
var result = new KeyValuePair<TKey, TValue>[items.Length - 1]; |
|
||||
|
|
||||
Array.Copy(items, 0, result, 0, index); |
|
||||
Array.Copy(items, index + 1, result, index, items.Length - index - 1); |
|
||||
|
|
||||
return Create<TArray>(result); |
|
||||
} |
|
||||
|
|
||||
private TArray Self<TArray>() where TArray : ArrayDictionary<TKey, TValue> |
|
||||
{ |
|
||||
return (this as TArray)!; |
|
||||
} |
|
||||
|
|
||||
private TArray Create<TArray>(KeyValuePair<TKey, TValue>[] newItems) where TArray : ArrayDictionary<TKey, TValue> |
|
||||
{ |
|
||||
if (ReferenceEquals(items, newItems)) |
|
||||
{ |
|
||||
return Self<TArray>(); |
|
||||
} |
|
||||
|
|
||||
var newClone = (TArray)MemberwiseClone(); |
|
||||
|
|
||||
newClone.items = newItems; |
|
||||
|
|
||||
return newClone; |
|
||||
} |
|
||||
|
|
||||
public bool ContainsKey(TKey key) |
|
||||
{ |
|
||||
var index = IndexOf(key); |
|
||||
|
|
||||
return index >= 0; |
|
||||
} |
|
||||
|
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) |
|
||||
{ |
|
||||
var index = IndexOf(key); |
|
||||
|
|
||||
if (index >= 0) |
|
||||
{ |
|
||||
value = items[index].Value; |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
value = default!; |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private int IndexOf(TKey key) |
|
||||
{ |
|
||||
for (var i = 0; i < items.Length; i++) |
|
||||
{ |
|
||||
if (keyComparer.Equals(items[i].Key, key)) |
|
||||
{ |
|
||||
return i; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return -1; |
|
||||
} |
|
||||
|
|
||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() |
|
||||
{ |
|
||||
return GetEnumerable(items).GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||
{ |
|
||||
return items.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
private static IEnumerable<T2> GetEnumerable<T2>(IEnumerable<T2> array) |
|
||||
{ |
|
||||
return array; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,150 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
|
||||
|
#pragma warning disable IDE0044 // Add readonly modifier
|
||||
|
#pragma warning disable RECS0108 // Warns about static fields in generic types
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.Collections |
||||
|
{ |
||||
|
public class ImmutableDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TKey : notnull |
||||
|
{ |
||||
|
private static readonly Dictionary<TKey, TValue> EmptyInner = new Dictionary<TKey, TValue>(); |
||||
|
private Dictionary<TKey, TValue> inner; |
||||
|
|
||||
|
public TValue this[TKey key] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (!TryGetValue(key, out var value)) |
||||
|
{ |
||||
|
throw new KeyNotFoundException(); |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<TKey> Keys |
||||
|
{ |
||||
|
get { return inner.Keys; } |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<TValue> Values |
||||
|
{ |
||||
|
get { return inner.Values; } |
||||
|
} |
||||
|
|
||||
|
public int Count |
||||
|
{ |
||||
|
get { return inner.Count; } |
||||
|
} |
||||
|
|
||||
|
public ImmutableDictionary() |
||||
|
: this(EmptyInner) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public ImmutableDictionary(Dictionary<TKey, TValue> inner) |
||||
|
{ |
||||
|
Guard.NotNull(inner); |
||||
|
|
||||
|
this.inner = inner; |
||||
|
} |
||||
|
|
||||
|
public ImmutableDictionary<TKey, TValue> With(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) |
||||
|
{ |
||||
|
return With<ImmutableDictionary<TKey, TValue>>(key, value, valueComparer); |
||||
|
} |
||||
|
|
||||
|
public TArray With<TArray>(TKey key, TValue value, IEqualityComparer<TValue>? valueComparer = null) where TArray : ImmutableDictionary<TKey, TValue> |
||||
|
{ |
||||
|
if (!TryGetValue(key, out var found) || !IsEqual(value, found, valueComparer)) |
||||
|
{ |
||||
|
var clone = new Dictionary<TKey, TValue>(inner) |
||||
|
{ |
||||
|
[key] = value |
||||
|
}; |
||||
|
|
||||
|
return Create<TArray>(clone); |
||||
|
} |
||||
|
|
||||
|
return Self<TArray>(); |
||||
|
} |
||||
|
|
||||
|
private static bool IsEqual(TValue lhs, TValue rhs, IEqualityComparer<TValue>? comparer = null) |
||||
|
{ |
||||
|
return comparer == null || comparer.Equals(lhs, rhs); |
||||
|
} |
||||
|
|
||||
|
public ImmutableDictionary<TKey, TValue> Without(TKey key) |
||||
|
{ |
||||
|
return Without<ImmutableDictionary<TKey, TValue>>(key); |
||||
|
} |
||||
|
|
||||
|
public TArray Without<TArray>(TKey key) where TArray : ImmutableDictionary<TKey, TValue> |
||||
|
{ |
||||
|
if (!inner.ContainsKey(key)) |
||||
|
{ |
||||
|
return Self<TArray>(); |
||||
|
} |
||||
|
|
||||
|
if (Count == 1) |
||||
|
{ |
||||
|
return Create<TArray>(EmptyInner); |
||||
|
} |
||||
|
|
||||
|
var clone = new Dictionary<TKey, TValue>(inner); |
||||
|
|
||||
|
clone.Remove(key); |
||||
|
|
||||
|
return Create<TArray>(clone); |
||||
|
} |
||||
|
|
||||
|
private TArray Self<TArray>() where TArray : ImmutableDictionary<TKey, TValue> |
||||
|
{ |
||||
|
return (this as TArray)!; |
||||
|
} |
||||
|
|
||||
|
private TArray Create<TArray>(Dictionary<TKey, TValue> clone) where TArray : ImmutableDictionary<TKey, TValue> |
||||
|
{ |
||||
|
var newClone = (TArray)MemberwiseClone(); |
||||
|
|
||||
|
newClone.inner = clone; |
||||
|
|
||||
|
return newClone; |
||||
|
} |
||||
|
|
||||
|
public bool ContainsKey(TKey key) |
||||
|
{ |
||||
|
return inner.ContainsKey(key); |
||||
|
} |
||||
|
|
||||
|
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) |
||||
|
{ |
||||
|
return inner.TryGetValue(key, out value); |
||||
|
} |
||||
|
|
||||
|
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() |
||||
|
{ |
||||
|
return GetEnumerable(inner).GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
{ |
||||
|
return inner.GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
private static IEnumerable<TItem> GetEnumerable<TItem>(IEnumerable<TItem> collection) |
||||
|
{ |
||||
|
return collection; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue