1: using System;
2: using System.Text.RegularExpressions;
3: using System.Collections;
4: using System.Globalization;
5: using System.ComponentModel;
6:
7: [assembly: CLSCompliant(true)]
8: namespace StringInject
9: {
10: public static class StringInjectExtension
11: {
12: /// <summary>
13: /// Extension method that replaces keys in a string with the values of matching object properties.
14: /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
15: /// </summary>
16: /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
17: /// <param name="injectionObject">The object whose properties should be injected in the string</param>
18: /// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
19: public static string Inject(this string formatString, object injectionObject)
20: {
21: return formatString.Inject(GetPropertyHash(injectionObject));
22: }
23:
24: /// <summary>
25: /// Extension method that replaces keys in a string with the values of matching dictionary entries.
26: /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
27: /// </summary>
28: /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
29: /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
30: /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
31: public static string Inject(this string formatString, IDictionary dictionary)
32: {
33: return formatString.Inject(new Hashtable(dictionary));
34: }
35:
36: /// <summary>
37: /// Extension method that replaces keys in a string with the values of matching hashtable entries.
38: /// <remarks>Uses <see cref="String.Format()"/> internally; custom formats should match those used for that method.</remarks>
39: /// </summary>
40: /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
41: /// <param name="attributes">A <see cref="Hashtable"/> with keys and values to inject into the string</param>
42: /// <returns>A version of the formatString string with hastable keys replaced by (formatted) key values.</returns>
43: public static string Inject(this string formatString, Hashtable attributes)
44: {
45: string result = formatString;
46: if (attributes == null || formatString == null)
47: return result;
48:
49: foreach (string attributeKey in attributes.Keys)
50: {
51: result = result.InjectSingleValue(attributeKey, attributes[attributeKey]);
52: }
53: return result;
54: }
55:
56: /// <summary>
57: /// Replaces all instances of a 'key' (e.g. {foo} or {foo:SomeFormat}) in a string with an optionally formatted value, and returns the result.
58: /// </summary>
59: /// <param name="formatString">The string containing the key; unformatted ({foo}), or formatted ({foo:SomeFormat})</param>
60: /// <param name="key">The key name (foo)</param>
61: /// <param name="replacementValue">The replacement value; if null is replaced with an empty string</param>
62: /// <returns>The input string with any instances of the key replaced with the replacement value</returns>
63: public static string InjectSingleValue(this string formatString, string key, object replacementValue)
64: {
65: string result = formatString;
66: //regex replacement of key with value, where the generic key format is:
67: //Regex foo = new Regex("{(foo)(?:}|(?::(.[^}]*)}))");
68: Regex attributeRegex = new Regex("{(" + key + ")(?:}|(?::(.[^}]*)}))"); //for key = foo, matches {foo} and {foo:SomeFormat}
69:
70: //loop through matches, since each key may be used more than once (and with a different format string)
71: foreach (Match m in attributeRegex.Matches(formatString))
72: {
73: string replacement = m.ToString();
74: if (m.Groups[2].Length > 0) //matched {foo:SomeFormat}
75: {
76: //do a double string.Format - first to build the proper format string, and then to format the replacement value
77: string attributeFormatString = string.Format(CultureInfo.InvariantCulture, "{{0:{0}}}", m.Groups[2]);
78: replacement = string.Format(CultureInfo.CurrentCulture, attributeFormatString, replacementValue);
79: }
80: else //matched {foo}
81: {
82: replacement = (replacementValue ?? string.Empty).ToString();
83: }
84: //perform replacements, one match at a time
85: result = result.Replace(m.ToString(), replacement); //attributeRegex.Replace(result, replacement, 1);
86: }
87: return result;
88:
89: }
90:
91:
92: /// <summary>
93: /// Creates a HashTable based on current object state.
94: /// <remarks>Copied from the MVCToolkit HtmlExtensionUtility class</remarks>
95: /// </summary>
96: /// <param name="properties">The object from which to get the properties</param>
97: /// <returns>A <see cref="Hashtable"/> containing the object instance's property names and their values</returns>
98: private static Hashtable GetPropertyHash(object properties)
99: {
100: Hashtable values = null;
101: if (properties != null)
102: {
103: values = new Hashtable();
104: PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
105: foreach (PropertyDescriptor prop in props)
106: {
107: values.Add(prop.Name, prop.GetValue(properties));
108: }
109: }
110: return values;
111: }
112:
113: }
114: }