From 81ddda7b9643f12ae7cd19463cf44882b9fb303b Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 25 Oct 2017 23:46:15 -0700 Subject: [PATCH] Add MetadataCollection --- .../Address.cs | 3 +- .../Endpoint.cs | 3 +- .../MetadataCollection.cs | 104 ++++++++++++++++++ .../TemplateAddress.cs | 4 +- .../TemplateEndpoint.cs | 6 +- .../MetadataCollectionTest.cs | 79 +++++++++++++ 6 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Dispatcher.Abstractions/MetadataCollection.cs create mode 100644 test/Microsoft.AspNetCore.Dispatcher.Abstractions.Test/MetadataCollectionTest.cs diff --git a/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Address.cs b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Address.cs index a65cb0b05e..2fa8e6372a 100644 --- a/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Address.cs +++ b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Address.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Diagnostics; namespace Microsoft.AspNetCore.Dispatcher @@ -11,6 +10,6 @@ namespace Microsoft.AspNetCore.Dispatcher { public abstract string DisplayName { get; } - public abstract IReadOnlyList Metadata { get; } + public abstract MetadataCollection Metadata { get; } } } diff --git a/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Endpoint.cs b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Endpoint.cs index ed3d96df58..4ddfdfdcb2 100644 --- a/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Endpoint.cs +++ b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/Endpoint.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using System.Diagnostics; namespace Microsoft.AspNetCore.Dispatcher @@ -11,6 +10,6 @@ namespace Microsoft.AspNetCore.Dispatcher { public abstract string DisplayName { get; } - public abstract IReadOnlyList Metadata { get; } + public abstract MetadataCollection Metadata { get; } } } diff --git a/src/Microsoft.AspNetCore.Dispatcher.Abstractions/MetadataCollection.cs b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/MetadataCollection.cs new file mode 100644 index 0000000000..8fb0ced2c2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Dispatcher.Abstractions/MetadataCollection.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNetCore.Dispatcher +{ + public class MetadataCollection : IReadOnlyList + { + private readonly object[] _items; + + public MetadataCollection() + { + _items = Array.Empty(); + } + + public MetadataCollection(IEnumerable items) + { + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + + _items = items.ToArray(); + } + + public object this[int index] => _items[index]; + + public int Count => _items.Length; + + public T GetMetadata() where T : class + { + for (var i = _items.Length -1; i >= 0; i--) + { + var item = _items[i] as T; + if (item !=null) + { + return item; + } + } + + return default; + } + + public IEnumerable GetOrderedMetadata() where T : class + { + for (var i = 0; i < _items.Length; i++) + { + var item = _items[i] as T; + if (item != null) + { + yield return item; + } + } + } + + public Enumerator GetEnumerator() => new Enumerator(this); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator : IEnumerator + { + private object[] _items; + private int _index; + private object _current; + + internal Enumerator(MetadataCollection collection) + { + _items = collection._items; + _index = 0; + _current = null; + } + + public object Current => _current; + + public void Dispose() + { + } + + public bool MoveNext() + { + if (_index < _items.Length) + { + _current = _items[_index++]; + return true; + } + + _current = null; + return false; + } + + public void Reset() + { + _index = 0; + _current = null; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Dispatcher/TemplateAddress.cs b/src/Microsoft.AspNetCore.Dispatcher/TemplateAddress.cs index 3053d0d936..139dd2c8e6 100644 --- a/src/Microsoft.AspNetCore.Dispatcher/TemplateAddress.cs +++ b/src/Microsoft.AspNetCore.Dispatcher/TemplateAddress.cs @@ -29,12 +29,12 @@ namespace Microsoft.AspNetCore.Dispatcher Template = template; Defaults = new DispatcherValueCollection(values); DisplayName = displayName; - Metadata = metadata.ToArray(); + Metadata = new MetadataCollection(metadata); } public override string DisplayName { get; } - public override IReadOnlyList Metadata { get; } + public override MetadataCollection Metadata { get; } public string Template { get; } diff --git a/src/Microsoft.AspNetCore.Dispatcher/TemplateEndpoint.cs b/src/Microsoft.AspNetCore.Dispatcher/TemplateEndpoint.cs index 3618e726c4..ebe8a5aedb 100644 --- a/src/Microsoft.AspNetCore.Dispatcher/TemplateEndpoint.cs +++ b/src/Microsoft.AspNetCore.Dispatcher/TemplateEndpoint.cs @@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Dispatcher HttpMethod = httpMethod; HandlerFactory = (next) => requestDelegate; DisplayName = displayName; - Metadata = metadata.ToArray(); + Metadata = new MetadataCollection(metadata); } public TemplateEndpoint( @@ -109,14 +109,14 @@ namespace Microsoft.AspNetCore.Dispatcher HttpMethod = httpMethod; HandlerFactory = delegateFactory; DisplayName = displayName; - Metadata = metadata.ToArray(); + Metadata = new MetadataCollection(metadata); } public override string DisplayName { get; } public string HttpMethod { get; } - public override IReadOnlyList Metadata { get; } + public override MetadataCollection Metadata { get; } public Func HandlerFactory { get; } diff --git a/test/Microsoft.AspNetCore.Dispatcher.Abstractions.Test/MetadataCollectionTest.cs b/test/Microsoft.AspNetCore.Dispatcher.Abstractions.Test/MetadataCollectionTest.cs new file mode 100644 index 0000000000..d76bcf16d3 --- /dev/null +++ b/test/Microsoft.AspNetCore.Dispatcher.Abstractions.Test/MetadataCollectionTest.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.Dispatcher +{ + public class MetadataCollectionTest + { + [Fact] + public void GetMetadata_ReturnsLastMatch() + { + // Arrange + var items = new object[] + { + new AuthNMetadata(), + new AuthZMetadata(), + new AuthNMetadata(), + }; + + var collection = new MetadataCollection(items); + + // Act + var result = collection.GetMetadata(); + + // Assert + Assert.Same(items[2], result); + } + + [Fact] + public void GetOrderedMetadata_ReturnsAllMatches() + { + // Arrange + var items = new object[] + { + new AuthNMetadata(), + new AuthZMetadata(), + new AuthNMetadata(), + }; + + var collection = new MetadataCollection(items); + + // Act + var result = collection.GetOrderedMetadata(); + + // Assert + Assert.Equal(new object[] { items[0], items[2] }, result); + } + + [Fact] + public void GetEnumerator_IncludesAllItems() + { + // Arrange + var items = new object[] + { + new AuthNMetadata(), + new AuthZMetadata(), + new AuthNMetadata(), + }; + + var collection = new MetadataCollection(items); + + // Act + var result = collection.ToArray(); + + // Assert + Assert.Equal(items, result); + } + + private class AuthNMetadata + { + } + + private class AuthZMetadata + { + } + } +}