Revision: 24048
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at February 19, 2010 12:16 by jmcd
Initial Code
class Program
{
static void Main()
{
var someObject = new { FirstName = "John", LastName = "McDowall", Dob = new DateTime(1976, 1, 14) };
var format = new StringFormatCompiler().Compile("{FirstName} {LastName}, born {Dob:D}");
var output = new Formatter().Process(format, someObject);
Console.WriteLine(output);
}
}
public class Formatter
{
public string Process(StringFormatWithNamedArguments formatWithNamedArguments, object valueProvider)
{
var valueList = new object[formatWithNamedArguments.ArgumentNames.Count];
for (var index = 0; index < valueList.Length; index++)
{
var argumentName = formatWithNamedArguments.ArgumentNames[index];
var value = GetValue(valueProvider, argumentName);
valueList[index] = value;
}
return string.Format(formatWithNamedArguments.Format, valueList);
}
private static object GetValue(object valueProvider, string name)
{
var type = valueProvider.GetType();
var propertyInfoOrDefault = type.GetProperties().Where(x => x.Name == name).SingleOrDefault();
if (propertyInfoOrDefault != null)
{
var methodInfo = propertyInfoOrDefault.GetGetMethod(true);
if (methodInfo != null)
{
return methodInfo.Invoke(valueProvider, new object[] {});
}
}
var field = type.GetFields().Where(x => x.Name == name).SingleOrDefault();
if (field != null)
{
return field.GetValue(valueProvider);
}
throw new ArgumentException("Could not find get property or field for " + name + " on type " + type);
}
}
public class StringFormatWithNamedArguments
{
public string Format { get; private set; }
public List<string> ArgumentNames { get; private set; }
public StringFormatWithNamedArguments(string format, List<string> argumentNames)
{
Format = format;
ArgumentNames = argumentNames;
}
}
public class StringFormatCompiler
{
public StringFormatWithNamedArguments Compile(string format)
{
var tokens = Tokenize(format);
var stringBuilder = new StringBuilder();
var argumentNames = new List<string>();
var argumentIndex = 0;
foreach (var token in tokens)
{
if (token is Literal)
{
stringBuilder.Append(token);
}
else
{
var argument = (Argument) token;
stringBuilder.Append('{').Append(argumentIndex).Append(argument.GetSpecification()).Append('}');
argumentIndex++;
argumentNames.Add(argument.GetName());
}
}
return new StringFormatWithNamedArguments(stringBuilder.ToString(), argumentNames);
}
public IEnumerable<Token> Tokenize(string format)
{
var result = new List<Token> {new Literal()};
foreach (var c in format.ToCharArray())
{
if (result.Last() is Literal)
if (c == '{')
result.Add(new Argument());
else
result.Last().Add(c);
else
if (c == '}')
result.Add(new Literal());
else
result.Last().Add(c);
}
return result.Where(x => x.HasData());
}
}
public abstract class Token
{
protected readonly StringBuilder Data = new StringBuilder();
public void Add(char c)
{
Data.Append(c);
}
public bool HasData()
{
return Data.Length > 0;
}
public override string ToString()
{
return Data.ToString();
}
}
public class Literal : Token
{
}
public class Argument : Token
{
public bool HasSpecification()
{
return IndexOfSpecification() != -1;
}
public string GetSpecification()
{
var indexOfSpecification = IndexOfSpecification();
return indexOfSpecification == -1 ? null : Data.ToString().Substring(indexOfSpecification);
}
private int IndexOfSpecification()
{
for (var index = 0; index < Data.Length; index++)
{
var c = Data[index];
if (c == ':')
{
return index;
}
}
return -1;
}
public string GetName()
{
var indexOfSpecification = IndexOfSpecification();
return indexOfSpecification == -1 ? Data.ToString() : Data.ToString().Substring(0, indexOfSpecification);
}
}
Initial URL
Initial Description
Initial Title
String Format with Named Arguments
Initial Tags
format
Initial Language
C#