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