From 4434bed84fa0a5012e76fbe543e86e67fd816b7a Mon Sep 17 00:00:00 2001 From: "Kruger, Jackson" Date: Fri, 7 Jan 2022 10:35:02 -0600 Subject: [PATCH 1/2] Add ability to use orderers that order dictionaries and IEnumerables when introspecting --- .../Configurations/ConfigurationTest.cs | 29 +++- .../Orderers/AnonymousOrdererTest.cs | 67 ++++++++ .../Orderers/ComparableOrdererTest.cs | 145 ++++++++++++++++++ StatePrinter.Tests/StatePrinter.Tests.csproj | 2 + StatePrinter/Configurations/Configuration.cs | 75 +++++++-- .../FieldHarvesters/IFieldHarvester.cs | 4 +- .../FieldHarvesters/ProjectionHarvester.cs | 2 +- StatePrinter/IHandler.cs | 31 ++++ StatePrinter/Introspection/IntroSpector.cs | 16 +- StatePrinter/Orderers/AnonymousOrderer.cs | 49 ++++++ StatePrinter/Orderers/ComparableOrderer.cs | 48 ++++++ StatePrinter/Orderers/IEnumerableOrderer.cs | 31 ++++ StatePrinter/Orderers/OrdererHelpers.cs | 43 ++++++ StatePrinter/StatePrinter.csproj | 5 + .../ValueConverters/IValueConverter.cs | 7 +- 15 files changed, 531 insertions(+), 23 deletions(-) create mode 100644 StatePrinter.Tests/Orderers/AnonymousOrdererTest.cs create mode 100644 StatePrinter.Tests/Orderers/ComparableOrdererTest.cs create mode 100644 StatePrinter/IHandler.cs create mode 100644 StatePrinter/Orderers/AnonymousOrderer.cs create mode 100644 StatePrinter/Orderers/ComparableOrderer.cs create mode 100644 StatePrinter/Orderers/IEnumerableOrderer.cs create mode 100644 StatePrinter/Orderers/OrdererHelpers.cs diff --git a/StatePrinter.Tests/Configurations/ConfigurationTest.cs b/StatePrinter.Tests/Configurations/ConfigurationTest.cs index 129bb95..adc2a36 100644 --- a/StatePrinter.Tests/Configurations/ConfigurationTest.cs +++ b/StatePrinter.Tests/Configurations/ConfigurationTest.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; +using StatePrinting.Orderers; using StatePrinting.Configurations; using StatePrinting.FieldHarvesters; using StatePrinting.TestAssistance; @@ -32,7 +33,7 @@ namespace StatePrinting.Tests.Configurations class ConfigurationTest { [Test] - public void TryFind() + public void TryGetValueConverter() { var config = new Configuration(); config.Add(new StandardTypesConverter(null)); @@ -42,6 +43,27 @@ public void TryFind() Assert.IsTrue(h is StandardTypesConverter); } + [Test] + public void TryGetFieldHarvester() + { + var config = new Configuration(); + config.AddHandler(type => type == typeof(decimal), type => new List()); + + Assert.IsTrue(config.TryGetFieldHarvester(typeof(decimal), out var h)); + Assert.IsTrue(h is AnonymousHarvester); + } + + [Test] + public void TryGetEnumerableOrderer() + { + var config = new Configuration(); + var orderer = new ComparableOrderer(); + config.Add(orderer); + + Assert.IsTrue(config.TryGetEnumerableOrderer(typeof(decimal), out var h)); + Assert.AreEqual(h, orderer); + } + [Test] public void SettingNullValues() { @@ -53,11 +75,16 @@ public void SettingNullValues() Assert.Throws(() => sut.Add((IFieldHarvester)null)); Assert.Throws(() => sut.Add((IValueConverter)null)); + Assert.Throws(() => sut.Add((IEnumerableOrderer)null)); Assert.Throws(() => sut.AddHandler(null, t => new List())); Assert.Throws(() => sut.AddHandler(t => true, null)); Assert.Throws(() => sut.AddHandler(null, null)); + Assert.Throws(() => sut.AddOrderer(null, x => x)); + Assert.Throws(() => sut.AddOrderer(x => true, null)); + Assert.Throws(() => sut.AddOrderer(null, null)); + Assert.Throws(() => sut.Test.SetAreEqualsMethod((TestFrameworkAreEqualsMethod) null)); Assert.Throws(() => sut.Test.SetAutomaticTestRewrite(null)); } diff --git a/StatePrinter.Tests/Orderers/AnonymousOrdererTest.cs b/StatePrinter.Tests/Orderers/AnonymousOrdererTest.cs new file mode 100644 index 0000000..04656a9 --- /dev/null +++ b/StatePrinter.Tests/Orderers/AnonymousOrdererTest.cs @@ -0,0 +1,67 @@ +//// Copyright 2014 Kasper B. Graversen +//// +//// 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. + +using NUnit.Framework; +using StatePrinting.Configurations; +using System.Collections.Generic; +using System.Linq; + +namespace StatePrinting.Tests.Orderers +{ + [TestFixture] + class AnonymousOrdererTest + { + [Test] + public void AnonymousOrdererUsesOrderFuncForHandledType() + { + var cfg = ConfigurationHelper.GetStandardConfiguration(" "); + cfg.AddOrderer(type => type == typeof(List), enumerable => enumerable.Cast().Reverse()); + var statePrinter = new Stateprinter(cfg); + var list = new List() { 0, 1, 2 }; + + var actual = statePrinter.PrintObject(list); + + var expected = @"new List() +{ + [0] = 2 + [1] = 1 + [2] = 0 +}"; + Assert.AreEqual(expected, actual); + } + + [Test] + public void AnonymousOrdererDoesNotUseOrderFuncForUnhandledType() + { + var cfg = ConfigurationHelper.GetStandardConfiguration(" "); + cfg.AddOrderer(type => false, enumerable => null); + var statePrinter = new Stateprinter(cfg); + var list = new List { 1, 0 }; + + var actual = statePrinter.PrintObject(list); + + var expected = @"new List() +{ + [0] = 1 + [1] = 0 +}"; + Assert.AreEqual(expected, actual); + } + } +} diff --git a/StatePrinter.Tests/Orderers/ComparableOrdererTest.cs b/StatePrinter.Tests/Orderers/ComparableOrdererTest.cs new file mode 100644 index 0000000..1f6272c --- /dev/null +++ b/StatePrinter.Tests/Orderers/ComparableOrdererTest.cs @@ -0,0 +1,145 @@ +// Copyright 2014 Kasper B. Graversen +// +// 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. + +using NUnit.Framework; +using StatePrinting.Configurations; +using StatePrinting.Orderers; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace StatePrinting.Tests.Orderers +{ + [TestFixture] + class ComparableOrdererTest + { + [TestFixture] + class UnitTests + { + [TestCase(typeof(IEnumerable), ExpectedResult = true)] + [TestCase(typeof(int), ExpectedResult = false)] + [TestCase(typeof(List), ExpectedResult = true)] + [TestCase(typeof(IEnumerable), ExpectedResult = true)] + [TestCase(typeof(Dictionary), ExpectedResult = false)] + [TestCase(typeof(Dictionary), ExpectedResult = false)] + [TestCase(typeof(Dictionary), ExpectedResult = false)] + [TestCase(typeof(Dictionary.KeyCollection), ExpectedResult = true)] + [TestCase(typeof(NonGenericTypeImplementingIEnumerable), ExpectedResult = true)] + public bool CanHandleTypesImplementingIEnumerableOfIComparables(Type type) + { + var comparableOrderer = new ComparableOrderer(); + + return comparableOrderer.CanHandleType(type); + } + + [Test] + public void CanOrderArrayOfValueTypes() + { + var comparableOrderer = new ComparableOrderer(); + var ints = new object[] { 5, 1, 3, 4, 2 }; + + var ordered = comparableOrderer.Order(ints).Cast().ToArray(); + + var expected = new[] { 1, 2, 3, 4, 5 }; + CollectionAssert.AreEqual(expected, ordered); + } + + [Test] + public void CanOrderArrayOfReferenceTypes() + { + var comparableOrderer = new ComparableOrderer(); + var strings = new[] { "c", "a", "b" }; + + var ordered = comparableOrderer.Order(strings).Cast().ToArray(); + + var expected = new[] { "a", "b", "c" }; + CollectionAssert.AreEqual(expected, ordered); + } + + [Test] + public void CannotOrderArrayOfDifferentTypes() + { + var comparableOrderer = new ComparableOrderer(); + var array = new object[] { 1, "a" }; + + Assert.Throws(() => comparableOrderer.Order(array).Cast().ToArray()); + } + + class NonGenericTypeImplementingIEnumerable : IEnumerable + { + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + } + } + + [TestFixture] + public class IntegrationTests + { + [Test] + public void IsUsedForEnumerables() + { + Stateprinter statePrinter = GetStatePrinterWithComparableOrderer(); + var list = new List() { 3, 1, 2, 0 }; + + var actual = statePrinter.PrintObject(list); + + var expected = @"new List() +{ + [0] = 0 + [1] = 1 + [2] = 2 + [3] = 3 +}"; + Assert.AreEqual(expected, actual); + } + + [Test] + public void IsUsedForDictionaries() + { + var statePrinter = GetStatePrinterWithComparableOrderer(); + var dictionary = new Dictionary() + { + { 3, 1 }, + { 1, 2 }, + { 2, 3 }, + }; + + var actual = statePrinter.PrintObject(dictionary); + + var expected = @"new Dictionary() +{ + [1] = 2 + [2] = 3 + [3] = 1 +}"; + Assert.AreEqual(expected, actual); + } + + private static Stateprinter GetStatePrinterWithComparableOrderer() + { + var cfg = ConfigurationHelper.GetStandardConfiguration(" "); + cfg.Add(new ComparableOrderer()); + var statePrinter = new Stateprinter(cfg); + return statePrinter; + } + } + } +} diff --git a/StatePrinter.Tests/StatePrinter.Tests.csproj b/StatePrinter.Tests/StatePrinter.Tests.csproj index 05ce4e5..940f64e 100644 --- a/StatePrinter.Tests/StatePrinter.Tests.csproj +++ b/StatePrinter.Tests/StatePrinter.Tests.csproj @@ -68,6 +68,8 @@ + + diff --git a/StatePrinter/Configurations/Configuration.cs b/StatePrinter/Configurations/Configuration.cs index 2128dab..1c8517f 100644 --- a/StatePrinter/Configurations/Configuration.cs +++ b/StatePrinter/Configurations/Configuration.cs @@ -22,10 +22,12 @@ using System.Collections.ObjectModel; using System.Globalization; using System.Linq; +using StatePrinting.Orderers; using StatePrinting.FieldHarvesters; using StatePrinting.OutputFormatters; using StatePrinting.TestAssistance; using StatePrinting.ValueConverters; +using System.Collections; namespace StatePrinting.Configurations { @@ -152,6 +154,16 @@ public ReadOnlyCollection FieldHarvesters get { return new ReadOnlyCollection(fieldHarvesters.ToArray()); } } + readonly Stack enumerableOrderers = new Stack(); + + /// + /// Gets a view of the orderers + /// + public ReadOnlyCollection EnumerableOrderers + { + get { return new ReadOnlyCollection(enumerableOrderers.ToArray()); } + } + /// /// Add a configuration. Adding will override the existing behaviour only when the /// added handler handles a type that is already handleable by the current configuration. @@ -178,13 +190,26 @@ public Configuration Add(IFieldHarvester handler) return this; } + /// + /// Add an orderer. Adding will override the existing behaviour only when the + /// added orderer handles a type that is already handleable by the current configuration. + /// + public Configuration Add(IEnumerableOrderer orderer) + { + if (orderer == null) + throw new ArgumentNullException(nameof(orderer)); + + enumerableOrderers.Push(orderer); + return this; + } + /// /// Add an anonymous handler implementation. Adding will override the existing behaviour only when the /// added handler handles a type that is already handleable by the current configuration. /// public Configuration AddHandler(Func canHandleTypeFunc, Func> getFieldsFunc) { - if (canHandleTypeFunc== null) + if (canHandleTypeFunc == null) throw new ArgumentNullException("canHandleTypeFunc"); if (getFieldsFunc == null) @@ -194,6 +219,22 @@ public Configuration AddHandler(Func canHandleTypeFunc, Func + /// Add an anonymous orderer implementation. Adding will override the existing behaviour only when the + /// added orderer handles a type that is already handleable by the current configuration. + /// + public Configuration AddOrderer(Func canHandleTypeFunc, Func orderFunc) + { + if (canHandleTypeFunc == null) + throw new ArgumentNullException(nameof(canHandleTypeFunc)); + + if (orderFunc == null) + throw new ArgumentNullException(nameof(orderFunc)); + + enumerableOrderers.Push(new AnonymousOrderer(canHandleTypeFunc, orderFunc)); + return this; + } + readonly Dictionary converterLookup = new Dictionary(); /// @@ -201,12 +242,7 @@ public Configuration AddHandler(Func canHandleTypeFunc, Func public bool TryGetValueConverter(Type source, out IValueConverter result) { - if (!converterLookup.TryGetValue(source, out result)) - { - result = valueConverters.FirstOrDefault(x => x.CanHandleType(source)); - converterLookup.Add(source, result); - } - return result != null; + return TryGetHandler(source, valueConverters, out result, converterLookup); } /// @@ -214,8 +250,17 @@ public bool TryGetValueConverter(Type source, out IValueConverter result) /// public bool TryGetFieldHarvester(Type source, out IFieldHarvester result) { - result = fieldHarvesters.FirstOrDefault(x => x.CanHandleType(source)); - return result != null; + return TryGetHandler(source, fieldHarvesters, out result); + } + + readonly Dictionary ordererLookup = new Dictionary(); + + /// + /// Find an orderer for the type. Orderers are examined in the reverse order of adding and the first match is returned. + /// + public bool TryGetEnumerableOrderer(Type source, out IEnumerableOrderer result) + { + return TryGetHandler(source, enumerableOrderers, out result, ordererLookup); } ProjectionHarvester projection; @@ -229,5 +274,17 @@ public ProjectionHarvester Project } public Func FactoryFileRepository = () => new FileRepository(); + + private static bool TryGetHandler(Type source, Stack handlers, out T result, Dictionary cache = null) + where T : IHandler + { + if (cache == null || !cache.TryGetValue(source, out result)) + { + result = handlers.FirstOrDefault(x => x.CanHandleType(source)); + if (cache != null) cache.Add(source, result); + } + + return result != null; + } } } diff --git a/StatePrinter/FieldHarvesters/IFieldHarvester.cs b/StatePrinter/FieldHarvesters/IFieldHarvester.cs index b12a9ba..eca2990 100644 --- a/StatePrinter/FieldHarvesters/IFieldHarvester.cs +++ b/StatePrinter/FieldHarvesters/IFieldHarvester.cs @@ -25,10 +25,8 @@ namespace StatePrinting.FieldHarvesters /// /// A fieldharvester is a configuration part that given a type is able to harvest all fields on it. /// - public interface IFieldHarvester + public interface IFieldHarvester : IHandler { - bool CanHandleType(Type type); - List GetFields(Type type); } } \ No newline at end of file diff --git a/StatePrinter/FieldHarvesters/ProjectionHarvester.cs b/StatePrinter/FieldHarvesters/ProjectionHarvester.cs index 66f3f1a..78aa3e6 100644 --- a/StatePrinter/FieldHarvesters/ProjectionHarvester.cs +++ b/StatePrinter/FieldHarvesters/ProjectionHarvester.cs @@ -95,7 +95,7 @@ bool SelectStrategy( return false; } - bool IFieldHarvester.CanHandleType(Type type) + bool IHandler.CanHandleType(Type type) { if (SelectStrategy(type, excluders, Strategy.Excluder)) return true; if (SelectStrategy(type, includers, Strategy.Includer)) return true; diff --git a/StatePrinter/IHandler.cs b/StatePrinter/IHandler.cs new file mode 100644 index 0000000..743e705 --- /dev/null +++ b/StatePrinter/IHandler.cs @@ -0,0 +1,31 @@ +// Copyright 2014 Kasper B. Graversen +// +// 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. + +using System; + +namespace StatePrinting +{ + public interface IHandler + { + /// + /// Is the type covered by this handler. + /// + bool CanHandleType(Type type); + } +} diff --git a/StatePrinter/Introspection/IntroSpector.cs b/StatePrinter/Introspection/IntroSpector.cs index 58d1662..0fa9892 100644 --- a/StatePrinter/Introspection/IntroSpector.cs +++ b/StatePrinter/Introspection/IntroSpector.cs @@ -80,7 +80,7 @@ void Introspect(object source, Field field) if (IntrospectDictionaryWithSimpleKey(source as IDictionary, field, sourceType)) return; - if (IntrospectIEnumerable(source, field)) + if (IntrospectIEnumerable(source, field, sourceType)) return; IntrospectComplexType(source, field, sourceType); @@ -180,7 +180,12 @@ bool IntrospectDictionaryWithSimpleKey(IDictionary source, Field field, Type sou tokens.Add(new Token(TokenType.FieldnameWithTypeAndReference, field, null, optionReferenceInfo, source.GetType())); tokens.Add(StartDict); - var keys = source.Keys; + IEnumerable keys = source.Keys; + if (configuration.TryGetEnumerableOrderer(keys.GetType(), out var orderer)) + { + keys = orderer.Order(keys); + } + foreach (var key in keys) { var valueValue = source[key]; @@ -193,7 +198,7 @@ bool IntrospectDictionaryWithSimpleKey(IDictionary source, Field field, Type sou return true; } - private bool IntrospectIEnumerable(object source, Field field) + private bool IntrospectIEnumerable(object source, Field field, Type sourceType) { var enumerable = source as IEnumerable; if (enumerable == null) @@ -205,6 +210,11 @@ private bool IntrospectIEnumerable(object source, Field field) tokens.Add(new Token(TokenType.FieldnameWithTypeAndReference, field, null, optionReferenceInfo, source.GetType())); tokens.Add(StartList); + if (configuration.TryGetEnumerableOrderer(sourceType, out var orderer)) + { + enumerable = orderer.Order(enumerable); + } + int i = 0; foreach (var x in enumerable) { diff --git a/StatePrinter/Orderers/AnonymousOrderer.cs b/StatePrinter/Orderers/AnonymousOrderer.cs new file mode 100644 index 0000000..19e1182 --- /dev/null +++ b/StatePrinter/Orderers/AnonymousOrderer.cs @@ -0,0 +1,49 @@ +// Copyright 2014 Kasper B. Graversen +// +// 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. + +using System; +using System.Collections; + +namespace StatePrinting.Orderers +{ + /// + /// Applies an ordering to the specified types using the specified function. + /// + class AnonymousOrderer : IEnumerableOrderer + { + private readonly Func canHandleTypeFunc; + private readonly Func orderFunc; + + public AnonymousOrderer(Func canHandleTypeFunc, Func orderFunc) + { + this.canHandleTypeFunc = canHandleTypeFunc; + this.orderFunc = orderFunc; + } + + public bool CanHandleType(Type type) + { + return canHandleTypeFunc(type); + } + + public IEnumerable Order(IEnumerable source) + { + return orderFunc(source); + } + } +} diff --git a/StatePrinter/Orderers/ComparableOrderer.cs b/StatePrinter/Orderers/ComparableOrderer.cs new file mode 100644 index 0000000..93047b2 --- /dev/null +++ b/StatePrinter/Orderers/ComparableOrderer.cs @@ -0,0 +1,48 @@ +// Copyright 2014 Kasper B. Graversen +// +// 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. + +using System; +using System.Collections; +using System.Linq; + +namespace StatePrinting.Orderers +{ + /// + /// Orders any containing elements + /// implementing into ascending order. + /// + public class ComparableOrderer : IEnumerableOrderer + { + public bool CanHandleType(Type type) + { + return OrdererHelpers.ImplementsIEnumerableOf(type); + } + + public IEnumerable Order(IEnumerable source) + { + var comparableElements = source.OfType(); + if (comparableElements.Any()) + { + return comparableElements.OrderBy(x => x); + } + + return source; + } + } +} diff --git a/StatePrinter/Orderers/IEnumerableOrderer.cs b/StatePrinter/Orderers/IEnumerableOrderer.cs new file mode 100644 index 0000000..0346c5e --- /dev/null +++ b/StatePrinter/Orderers/IEnumerableOrderer.cs @@ -0,0 +1,31 @@ +// Copyright 2014 Kasper B. Graversen +// +// 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. + +using System.Collections; + +namespace StatePrinting.Orderers +{ + /// + /// A handler that can apply an ordering to a specific IEnumerable + /// + public interface IEnumerableOrderer : IHandler + { + IEnumerable Order(IEnumerable source); + } +} diff --git a/StatePrinter/Orderers/OrdererHelpers.cs b/StatePrinter/Orderers/OrdererHelpers.cs new file mode 100644 index 0000000..c25eea7 --- /dev/null +++ b/StatePrinter/Orderers/OrdererHelpers.cs @@ -0,0 +1,43 @@ +//// Copyright 2014 Kasper B. Graversen +//// +//// 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. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StatePrinting.Orderers +{ + public static class OrdererHelpers + { + /// + /// Returns whether the given implements a generic + /// whose elements are assignable to TElement + /// + public static bool ImplementsIEnumerableOf(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + return typeof(TElement).IsAssignableFrom(type.GetGenericArguments()[0]); + } + + var enumerables = type.GetInterfaces().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + return enumerables.Any(e => typeof(TElement).IsAssignableFrom(e.GetGenericArguments()[0])); + } + } +} diff --git a/StatePrinter/StatePrinter.csproj b/StatePrinter/StatePrinter.csproj index 6452c38..8ff1fe5 100644 --- a/StatePrinter/StatePrinter.csproj +++ b/StatePrinter/StatePrinter.csproj @@ -58,6 +58,10 @@ + + + + @@ -74,6 +78,7 @@ + diff --git a/StatePrinter/ValueConverters/IValueConverter.cs b/StatePrinter/ValueConverters/IValueConverter.cs index 6fbb5a4..4458296 100644 --- a/StatePrinter/ValueConverters/IValueConverter.cs +++ b/StatePrinter/ValueConverters/IValueConverter.cs @@ -24,13 +24,8 @@ namespace StatePrinting.ValueConverters /// /// A handler that is able to convert a value of specific types to on a single line string. /// - public interface IValueConverter + public interface IValueConverter : IHandler { - /// - /// Is the type covered by this handler. - /// - bool CanHandleType(Type type); - /// /// Convert objects of handled types into a simple one-line representation. /// From e1613b33b58532562f1787fe305685fa6c40aa4e Mon Sep 17 00:00:00 2001 From: "Kruger, Jackson" Date: Fri, 7 Jan 2022 11:13:01 -0600 Subject: [PATCH 2/2] Fix test TryGetEnumerableOrderer --- StatePrinter.Tests/Configurations/ConfigurationTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StatePrinter.Tests/Configurations/ConfigurationTest.cs b/StatePrinter.Tests/Configurations/ConfigurationTest.cs index adc2a36..bedc92b 100644 --- a/StatePrinter.Tests/Configurations/ConfigurationTest.cs +++ b/StatePrinter.Tests/Configurations/ConfigurationTest.cs @@ -60,7 +60,7 @@ public void TryGetEnumerableOrderer() var orderer = new ComparableOrderer(); config.Add(orderer); - Assert.IsTrue(config.TryGetEnumerableOrderer(typeof(decimal), out var h)); + Assert.IsTrue(config.TryGetEnumerableOrderer(typeof(IEnumerable), out var h)); Assert.AreEqual(h, orderer); }