BindingKey.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Security.Principal;
  7. using Abc.Zebus.Util.Annotations;
  8. using Abc.Zebus.Util.Extensions;
  9. using ProtoBuf;
  10. namespace Abc.Zebus.Routing
  11. {
  12. [ProtoContract]
  13. public struct BindingKey : IEquatable<BindingKey>
  14. {
  15. private const string _star = "*";
  16. private const string _sharp = "#";
  17. public static readonly BindingKey Empty = new BindingKey();
  18. private static readonly ConcurrentDictionary<Type, BindingKeyBuilder> _builders = new ConcurrentDictionary<Type, BindingKeyBuilder>();
  19. private static readonly Func<Type, BindingKeyBuilder> _bindingKeyBuilderFactory = CreateBuilder;
  20. [ProtoMember(1, IsRequired = true)]
  21. private readonly string[] _parts;
  22. public BindingKey(params string[] parts)
  23. {
  24. if (parts == null || parts.Length == 0)
  25. _parts = null;
  26. else
  27. _parts = parts;
  28. }
  29. public int PartCount => _parts?.Length ?? 0;
  30. public bool IsEmpty => _parts == null || _parts.Length == 1 && IsSharp(0);
  31. public bool IsSharp(int index)
  32. {
  33. return _parts[index] == _sharp;
  34. }
  35. public bool IsStar(int index)
  36. {
  37. return _parts[index] == _star;
  38. }
  39. [Pure]
  40. public string GetPart(int index) => index < PartCount ? _parts[index] : null;
  41. [Pure]
  42. public IEnumerable<string> GetParts() => _parts?.ToList() ?? Enumerable.Empty<string>();
  43. public override bool Equals(object obj)
  44. {
  45. var other = obj as BindingKey?;
  46. return other != null && Equals(other.Value);
  47. }
  48. public bool Equals(BindingKey other)
  49. {
  50. if (Equals(_parts, other._parts))
  51. return true;
  52. if (_parts == null || other._parts == null || _parts.Length != other._parts.Length)
  53. return false;
  54. for (var partIndex = 0; partIndex < _parts.Length; ++partIndex)
  55. {
  56. if (!string.Equals(_parts[partIndex], other._parts[partIndex]))
  57. return false;
  58. }
  59. return true;
  60. }
  61. public override int GetHashCode()
  62. {
  63. if (_parts == null || _parts.Length == 0)
  64. return 0;
  65. var hashCode = _parts[0].GetHashCode();
  66. for (var partIndex = 1; partIndex < _parts.Length; ++partIndex)
  67. {
  68. hashCode = (hashCode * 397) ^ _parts[partIndex].GetHashCode();
  69. }
  70. return hashCode;
  71. }
  72. public override string ToString()
  73. {
  74. if (_parts == null)
  75. return _sharp;
  76. return string.Join(".", _parts);
  77. }
  78. internal static BindingKey Create(IMessage message)
  79. => GetBindingKeyBuilder(message.GetType())?.BuildKey(message) ?? Empty;
  80. internal static BindingKey Create(Type messageType, IDictionary<string, string> fieldValues)
  81. => GetBindingKeyBuilder(messageType)?.BuildKey(fieldValues) ?? Empty;
  82. private static BindingKeyBuilder GetBindingKeyBuilder(Type messageType)
  83. => _builders.GetOrAdd(messageType, _bindingKeyBuilderFactory);
  84. private static BindingKeyBuilder CreateBuilder(Type messageType)
  85. {
  86. if (!Attribute.IsDefined(messageType, typeof(Routable)))
  87. return null;
  88. return new BindingKeyBuilder(messageType);
  89. }
  90. private class BindingKeyBuilder
  91. {
  92. private readonly BindingKeyToken[] _tokens;
  93. public BindingKeyBuilder(Type messageType)
  94. {
  95. var fields = from field in messageType.GetFields(BindingFlags.Public | BindingFlags.Instance)
  96. let attributes = field.GetCustomAttributes(typeof(RoutingPositionAttribute), true)
  97. where attributes.Length == 1
  98. select new BindingKeyToken(((RoutingPositionAttribute)attributes[0]).Position, messageType, field);
  99. var properties = from property in messageType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
  100. let attributes = property.GetCustomAttributes(typeof(RoutingPositionAttribute), true)
  101. where attributes.Length == 1
  102. select new BindingKeyToken(((RoutingPositionAttribute)attributes[0]).Position, messageType, property);
  103. _tokens = fields.Concat(properties).OrderBy(x => x.Position).ToArray();
  104. }
  105. public BindingKey BuildKey(IMessage message)
  106. {
  107. var parts = new string[_tokens.Length];
  108. for (var tokenIndex = 0; tokenIndex < parts.Length; ++tokenIndex)
  109. {
  110. parts[tokenIndex] = _tokens[tokenIndex].GetValue(message);
  111. }
  112. return new BindingKey(parts);
  113. }
  114. public BindingKey BuildKey(IDictionary<string, string> fieldValues)
  115. {
  116. var parts = new string[_tokens.Length];
  117. for (var tokenIndex = 0; tokenIndex < _tokens.Length; ++tokenIndex)
  118. {
  119. parts[tokenIndex] = fieldValues.GetValueOrDefault(_tokens[tokenIndex].Name, _star);
  120. }
  121. return new BindingKey(parts);
  122. }
  123. }
  124. }
  125. }