Skip to content

Commit

Permalink
Fixes #313 (#314)
Browse files Browse the repository at this point in the history
ListFormatter:
In v1.6.1 a Selector was tested for having the name "index", even if data was not an IList, and returned the CollectionIndex. This is now implemented again in the ListFormatter.TryEvaluateSelector(...)
  • Loading branch information
axunonb committed Sep 18, 2022
1 parent 70d2cbd commit 9c1188e
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 24 deletions.
19 changes: 18 additions & 1 deletion src/SmartFormat.Tests/Extensions/ListFormatterTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
Expand Down Expand Up @@ -66,6 +67,22 @@ public void Null_List()
Assert.AreEqual(string.Empty, result);
}

[Test]
public void Enumerable_With_SelectorName_Index_Is_Recognized()
{
var smart = Smart.CreateDefaultSmartFormat();
var objects = new object[]
{
new { Content = "Content A" },
new { Content = "Content B" }
};

// Linq Select returns an IEnumerable.
// "Index" selector should be recognized even when the argument is not an IList
var result = smart.Format("{0:{Content} with Index {Index}|, }", objects.Select(x => x));
Assert.That(result, Is.EqualTo("Content A with Index 0, Content B with Index 1"));
}

[Test]
public void List_of_anonymous_types_and_enumerables()
{
Expand Down Expand Up @@ -292,4 +309,4 @@ public void Null_IList_Not_Nullable_Should_Throw()
Throws.Exception.TypeOf<FormattingException>().And.Message
.Contains("{Numbers[0]}"));
}
}
}
30 changes: 14 additions & 16 deletions src/SmartFormat/Extensions/ListFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections;
using System.Threading;
using SmartFormat.Core.Extensions;
using SmartFormat.Core.Formatting;
using SmartFormat.Core.Parsing;
using SmartFormat.Core.Settings;
using SmartFormat.Pooling.SmartPools;
Expand Down Expand Up @@ -76,8 +77,16 @@ public bool TryEvaluateSelector(ISelectorInfo selectorInfo)
var current = selectorInfo.CurrentValue;
var selector = selectorInfo.SelectorText;

if (current is not IList currentList) return false;
// Check whether the selector is named "Index"
var selectorIsIndex = current is IEnumerable && selector.Equals("index", StringComparison.OrdinalIgnoreCase);
if (selectorIsIndex && selectorInfo.SelectorIndex == 0)
{
selectorInfo.Result = CollectionIndex;
return true;
}

if (current is not IList currentList) return false;

// See if we're trying to access a specific index:
var isAbsolute = selectorInfo.SelectorIndex == 0 && selectorInfo.SelectorOperator.Length == 0;
if (!isAbsolute && int.TryParse(selector, out var itemIndex) &&
Expand All @@ -91,22 +100,11 @@ public bool TryEvaluateSelector(ISelectorInfo selectorInfo)
return true;
}

// We want to see if there is an "Index" property that was supplied.
if (selector.Equals("index", StringComparison.OrdinalIgnoreCase))
// Looking for 2 lists to sync: "{List1: {List2[Index]}}"
if (selectorIsIndex && 0 <= CollectionIndex && CollectionIndex < currentList.Count)
{
// Looking for "{Index}"
if (selectorInfo.SelectorIndex == 0)
{
selectorInfo.Result = CollectionIndex;
return true;
}

// Looking for 2 lists to sync: "{List1: {List2[Index]} }"
if (0 <= CollectionIndex && CollectionIndex < currentList.Count)
{
selectorInfo.Result = currentList[CollectionIndex];
return true;
}
selectorInfo.Result = currentList[CollectionIndex];
return true;
}

return false;
Expand Down
13 changes: 6 additions & 7 deletions src/SmartFormat/Extensions/ReflectionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public class ReflectionSource : Source
/// <inheritdoc />
public override bool TryEvaluateSelector(ISelectorInfo selectorInfo)
{

var current = selectorInfo.CurrentValue;

if (TrySetResultForNullableOperator(selectorInfo)) return true;
Expand Down Expand Up @@ -95,7 +94,7 @@ private bool EvaluateMembers(ISelectorInfo selectorInfo, string selector, object
switch (member.MemberType)
{
case MemberTypes.Field:
// Selector is a Field; retrieve the value:
// Selector is a Field; retrieve the value:
var field = member as FieldInfo;
selectorInfo.Result = field?.GetValue(current);
if (IsTypeCacheEnabled) TypeCache[(sourceType, selector)] = (field, null);
Expand All @@ -104,17 +103,17 @@ private bool EvaluateMembers(ISelectorInfo selectorInfo, string selector, object
case MemberTypes.Method:
if (!TryGetMethodInfo(member, out var method)) continue;

// Check that this method is valid -- it needs to return a value and has to be parameter-less:
// We are only looking for a parameter-less Function/Property:
// Check that this method is valid -- it needs to return a value and has to be parameter-less:
// We are only looking for a parameter-less Function/Property:
if (method?.GetParameters().Length > 0) continue;

// Make sure that this method is not void! It has to be a Function!
// Make sure that this method is not void! It has to be a Function!
if (method?.ReturnType == typeof(void)) continue;

// Add to cache
if (IsTypeCacheEnabled) TypeCache[(sourceType, selector)] = (null, method);

// Retrieve the Selectors/ParseFormat value:
// Retrieve the Selectors/ParseFormat value:
selectorInfo.Result = method?.Invoke(current, Array.Empty<object>());
return true;
}
Expand All @@ -141,4 +140,4 @@ private static bool TryGetMethodInfo(MemberInfo member, out MethodInfo? method)
method = member as MethodInfo;
return true;
}
}
}

0 comments on commit 9c1188e

Please sign in to comment.