Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to access array elements, and this[] accessors #57

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions mustache-sharp.test/FormatCompilerTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,27 @@ public DerivedClass()
public new T Value { get; set; }
}

/// <summary>
/// Access array elements and this
/// </summary>
[TestMethod]
public void TestCompile_Access_Arrays_And_This() {
FormatCompiler compiler = new FormatCompiler();
const string format = @"Hello, {{Array.[0]}} {{O.[1]}} {{O.[Good evening]}}!!!";
Generator generator = compiler.Compile(format);
string result = generator.Render(new { O = new ThisAccessor(), Array = new string[] { "Element 0", "Element 1" } });
Assert.AreEqual("Hello, Element 0 [1]=1 [Good evening]=Good evening!!!", result, "The wrong text was generated.");
}

public class ThisAccessor {
public string this[int i] {
get { return "[" + i + "]=" + i.ToString(); }
}

public string this[string s] {
get { return "[" + s + "]=" + s; }
}
}
#endregion

#region Comment
Expand Down
2 changes: 1 addition & 1 deletion mustache-sharp/FormatCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private static string getCommentTagRegex()

private static string getKeyRegex()
{
return @"((?<key>" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
return @"((?<key>@?" + RegexHelper.CompoundKeyOrArrayAccess + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
}

private static string getTagRegex(TagDefinition definition)
Expand Down
46 changes: 45 additions & 1 deletion mustache-sharp/PropertyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal sealed class PropertyDictionary : IDictionary<string, object>

private readonly object _instance;
private readonly Dictionary<string, Func<object, object>> _typeCache;
private PropertyInfo _intThis;
private PropertyInfo _stringThis;

/// <summary>
/// Initializes a new instance of a PropertyDictionary.
Expand All @@ -34,6 +36,8 @@ public PropertyDictionary(object instance)
{
_typeCache = getCacheType(_instance);
}
_intThis = _instance.GetType().GetProperty("Item", new Type[] { typeof(int) });
_stringThis = _instance.GetType().GetProperty("Item", new Type[] { typeof(string) });
}
}

Expand Down Expand Up @@ -119,6 +123,10 @@ public bool ContainsKey(string key)
return _typeCache.ContainsKey(key);
}

public bool HasIntThis { get { return _intThis != null; } }

public bool HasStringThis { get { return _stringThis != null; } }

/// <summary>
/// Gets the name of the properties in the type.
/// </summary>
Expand Down Expand Up @@ -152,7 +160,43 @@ public bool TryGetValue(string key, out object value)
return true;
}

/// <summary>
/// <summary>
/// Tries to get the this[key] value for the given property name.
/// </summary>
/// <param name="key">The index to use in the this method.</param>
/// <param name="value">The variable to store the value of the property or the default value if the property is not found.</param>
/// <returns>True if a result was found; false if an exception was thrown.</returns>
/// <exception cref="System.ArgumentNullException">The key was null.</exception>

public bool TryGetThisValue(int key, out object value) {
try {
value = _intThis.GetValue(_instance, new object[] { key });
return true;
} catch {
value = null;
return false;
}
}

/// <summary>
/// Tries to get the this[key] value for the given property name.
/// </summary>
/// <param name="key">The index to use in the this method.</param>
/// <param name="value">The variable to store the value of the property or the default value if the property is not found.</param>
/// <returns>True if a result was found; false if an exception was thrown.</returns>
/// <exception cref="System.ArgumentNullException">The key was null.</exception>

public bool TryGetThisValue(string key, out object value) {
try {
value = _stringThis.GetValue(_instance, new object[] { key });
return true;
} catch {
value = null;
return false;
}
}

/// <summary>
/// Gets the values of all of the properties in the object.
/// </summary>
public ICollection<object> Values
Expand Down
18 changes: 17 additions & 1 deletion mustache-sharp/RegexHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ internal static class RegexHelper
public const string String = @"'.*?'";
public const string Number = @"[-+]?\d*\.?\d+";
public const string CompoundKey = "@?" + Key + @"(?:\." + Key + ")*";
public const string Argument = @"(?:(?<arg_key>" + CompoundKey + @")|(?<arg_string>" + String + @")|(?<arg_number>" + Number + @"))";
public const string KeyOrArrayAccess = "(?:" + Key + @"|\[[^\]]+\]" + ")";
public const string CompoundKeyOrArrayAccess = KeyOrArrayAccess + @"(\." + KeyOrArrayAccess + ")*";
public const string Argument = @"(?:(?<arg_key>" + CompoundKey + @")|(?<arg_string>" + String + @")|(?<arg_number>" + Number + @"))";

public static bool IsInteger(string s) {
return Regex.IsMatch(s, @"^\d{1,10}$");
}

/// <summary>
/// Determines whether the given name is a legal identifier.
Expand All @@ -29,6 +35,16 @@ public static bool IsValidIdentifier(string name)
return regex.IsMatch(name);
}

public static string[] SplitKey(string compoundKey) {
MatchCollection m = Regex.Matches(compoundKey, KeyOrArrayAccess);
// Would prefer to use Linq for this, but MatchCollection does not seem to support it
string[] result = new string[m.Count];
for (int i = 0; i < result.Length; i++) {
result[i] = m[i].Value;
}
return result;
}

public static bool IsString(string value)
{
if (value == null)
Expand Down
31 changes: 28 additions & 3 deletions mustache-sharp/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public bool TryFind(string name, out object value)
private SearchResults tryFind(string name)
{
SearchResults results = new SearchResults();
results.Members = name.Split('.');
results.Members = RegexHelper.SplitKey(name);
results.MemberIndex = 0;
if (results.Member == "this")
{
Expand All @@ -199,7 +199,7 @@ private SearchResults tryFind(string name)
results.Lookup = toLookup(results.Value);
results.MemberIndex = index;
object value;
results.Found = results.Lookup.TryGetValue(results.Member, out value);
results.Found = tryLookup(results, out value);
results.Value = value;
}
return results;
Expand All @@ -209,7 +209,7 @@ private void tryFindFirst(SearchResults results)
{
results.Lookup = toLookup(_source);
object value;
if (results.Lookup.TryGetValue(results.Member, out value))
if (tryLookup(results, out value))
{
results.Found = true;
results.Value = value;
Expand All @@ -223,6 +223,31 @@ private void tryFindFirst(SearchResults results)
}
_parent.tryFindFirst(results);
}

private bool tryLookup(SearchResults results, out object value) {
string member = results.Member;
if (member.StartsWith("[")) {
value = null;
PropertyDictionary dic = results.Lookup as PropertyDictionary;
if (dic == null) return false;
member = member.Substring(1, member.Length - 2);
if (RegexHelper.IsInteger(member)) {
int i = int.Parse(member);
if (dic.HasIntThis) {
if (dic.TryGetThisValue(i, out value)) return true;
}
Array array = dic.Instance as Array;
if (array != null && i < array.GetUpperBound(0)) {
value = array.GetValue(i);
return true;
}
}
if(dic.HasStringThis) return (dic.TryGetThisValue(member, out value));
return false;
} else {
return results.Lookup.TryGetValue(member, out value);
}
}
}

internal class SearchResults
Expand Down