using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Tapeti.Helpers; namespace Tapeti.Default { /// /// /// IRoutingKeyStrategy implementation which transforms the class name into a dot-separated routing key based /// on the casing. Accounts for acronyms. If the class name ends with 'Message' it is not included in the routing key. /// /// /// ExampleClassNameMessage will result in example.class.name /// public class TypeNameRoutingKeyStrategy : IRoutingKeyStrategy { private const string SeparatorPattern = @" (? RoutingKeyCache = new ConcurrentDictionary(); /// public string GetRoutingKey(Type messageType) { return RoutingKeyCache.GetOrAdd(messageType, BuildRoutingKey); } /// /// Actual implementation of GetRoutingKey, called only when the type has not been cached yet. /// /// protected virtual string BuildRoutingKey(Type messageType) { return RoutingKeyHelper.Decorate(messageType, () => { // Split PascalCase into dot-separated parts. If the class name ends in "Message" leave that out. var words = SplitPascalCase(messageType.Name); if (words == null) return ""; if (words.Count > 1 && words.Last().Equals("Message", StringComparison.InvariantCultureIgnoreCase)) words.RemoveAt(words.Count - 1); return string.Join(".", words.Select(s => s.ToLower())); }); } private static List SplitPascalCase(string value) { var split = SeparatorRegex.Split(value); if (split.Length == 0) return null; var result = new List(split.Length - 1 / 2) { split[0] }; for (var i = 1; i < split.Length; i += 2) result.Add(split[i] + split[i + 1]); return result; } } }