I've got a strange problem using the Castle NVelocity...
If the template string ends with a $ character, it throws the following and exception:
Lexical error: NVelocity.Runtime.Parser.TokenMgrError: Lexical error at line 1, column 94. Encountered: after : ""
If i add a space, or any other character, to the end of the string it works as expected.
Does anybody have a workaround for this?
Here's some sample code that reproduces the problem:
class Program
{
static void Main(string[] args)
{
var result = Merge("The quick brown ${Animal} jumps over the lazy dog$", new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("Animal", "fox") });
Console.WriteLine("Success: {0}", result.Key);
Console.WriteLine("Message: {0}", result.Value);
Console.Read();
}
public static KeyValuePair<bool, string> Merge(string template, IList<KeyValuePair<string, string>> data)
{
var ret = new KeyValuePair<bool, string>(false, null);
try
{
if (data != null)
{
var engine = new VelocityEngine();
engine.Init();
var context = new VelocityContext();
foreach (var tokenData in data)
{
context.Put(tokenData.Key, tokenData.Value);
}
var templateContent = template;
var sb = new StringBuilder();
var textWriter = new StringWriter(sb);
engine.Evaluate(context, textWriter, String.Empty, templateContent);
ret = new KeyValuePair<bool, string>(true, sb.ToString());
}
}
catch (Exception ex)
{
ret = new KeyValuePair<bool, string>(false, ex.Message);
}
return ret;
}
}
You have a couple of options:
If you have no influence over the input string to be merged, make sure that they don't have a trailing dollar character, at least not for the merge process:
Example:
bool dollarAtEnd = false;
if (input.EndsWith('$'))
{
input += " ";
dollarAtEnd = true;
}
var result = Merge(input, ...);
if (dollarAtEnd)
{
result = result.Substring(1, result.Length - 1);
}
If you can control the input string, but only have the requirement that some of them should end with a dollar character, you can do as follows:
Example:
"#set($dollar='$')The quick brown ${Animal} jumps over the lazy dog$dollar"
Or pass "dollar" as variable to the VelocityContext, rather than specifying it inline.