| | 1 | | using Fluorite.Extensions; |
| | 2 | | using Fluorite.Strainer.Models.Metadata; |
| | 3 | |
|
| | 4 | | namespace Fluorite.Strainer.Services.Metadata.Attributes; |
| | 5 | |
|
| | 6 | | public class AttributeMetadataRetriever : IAttributeMetadataRetriever |
| | 7 | | { |
| | 8 | | private readonly IMetadataSourceChecker _metadataSourceChecker; |
| | 9 | | private readonly IAttributePropertyMetadataBuilder _attributePropertyMetadataBuilder; |
| | 10 | | private readonly IPropertyMetadataDictionaryProvider _propertyMetadataDictionaryProvider; |
| | 11 | | private readonly IStrainerAttributeProvider _strainerAttributeProvider; |
| | 12 | | private readonly IPropertyInfoProvider _propertyInfoProvider; |
| | 13 | | private readonly IAttributeCriteriaChecker _attributeCriteriaChecker; |
| | 14 | |
|
| 20 | 15 | | public AttributeMetadataRetriever( |
| 20 | 16 | | IMetadataSourceChecker metadataSourceChecker, |
| 20 | 17 | | IAttributePropertyMetadataBuilder attributePropertyMetadataBuilder, |
| 20 | 18 | | IPropertyMetadataDictionaryProvider propertyMetadataDictionaryProvider, |
| 20 | 19 | | IStrainerAttributeProvider strainerAttributeProvider, |
| 20 | 20 | | IPropertyInfoProvider propertyInfoProvider, |
| 20 | 21 | | IAttributeCriteriaChecker attributeCriteriaChecker) |
| | 22 | | { |
| 20 | 23 | | _metadataSourceChecker = Guard.Against.Null(metadataSourceChecker); |
| 20 | 24 | | _attributePropertyMetadataBuilder = Guard.Against.Null(attributePropertyMetadataBuilder); |
| 20 | 25 | | _strainerAttributeProvider = Guard.Against.Null(strainerAttributeProvider); |
| 20 | 26 | | _propertyInfoProvider = Guard.Against.Null(propertyInfoProvider); |
| 20 | 27 | | _propertyMetadataDictionaryProvider = Guard.Against.Null(propertyMetadataDictionaryProvider); |
| 20 | 28 | | _attributeCriteriaChecker = Guard.Against.Null(attributeCriteriaChecker); |
| 20 | 29 | | } |
| | 30 | |
|
| | 31 | | public IPropertyMetadata? GetDefaultMetadataFromObjectAttribute(Type modelType) |
| | 32 | | { |
| 3 | 33 | | Guard.Against.Null(modelType); |
| | 34 | |
|
| 3 | 35 | | if (!IsMetadataSourceEnabled(MetadataSourceType.ObjectAttributes)) |
| | 36 | | { |
| 1 | 37 | | return null; |
| | 38 | | } |
| | 39 | |
|
| 2 | 40 | | var currentType = modelType; |
| | 41 | |
|
| | 42 | | do |
| | 43 | | { |
| 2 | 44 | | var attribute = _strainerAttributeProvider.GetObjectAttribute(currentType); |
| | 45 | |
|
| 2 | 46 | | if (attribute != null && attribute.DefaultSortingPropertyName != null) |
| | 47 | | { |
| 2 | 48 | | var propertyInfo = _propertyInfoProvider.GetPropertyInfo(modelType, attribute.DefaultSortingPropertyName |
| 2 | 49 | | if (propertyInfo == null) |
| | 50 | | { |
| 1 | 51 | | throw new InvalidOperationException( |
| 1 | 52 | | $"Could not find property {attribute.DefaultSortingPropertyName} " + |
| 1 | 53 | | $"in type {modelType.FullName} marked as its default " + |
| 1 | 54 | | $"sorting property. Ensure that such property exists in " + |
| 1 | 55 | | $"{modelType.FullName} and it's accessible."); |
| | 56 | | } |
| | 57 | |
|
| 1 | 58 | | return _attributePropertyMetadataBuilder.BuildDefaultMetadata(attribute, propertyInfo); |
| | 59 | | } |
| | 60 | |
|
| 0 | 61 | | currentType = currentType.BaseType; |
| | 62 | |
|
| | 63 | | } |
| 0 | 64 | | while (currentType != typeof(object) && currentType != typeof(ValueType)); |
| | 65 | |
|
| 0 | 66 | | return null; |
| | 67 | | } |
| | 68 | |
|
| | 69 | | public IPropertyMetadata? GetDefaultMetadataFromPropertyAttribute(Type modelType) |
| | 70 | | { |
| 3 | 71 | | Guard.Against.Null(modelType); |
| | 72 | |
|
| 3 | 73 | | if (!IsMetadataSourceEnabled(MetadataSourceType.PropertyAttributes)) |
| | 74 | | { |
| 1 | 75 | | return null; |
| | 76 | | } |
| | 77 | |
|
| 2 | 78 | | var attribute = _propertyInfoProvider |
| 2 | 79 | | .GetPropertyInfos(modelType) |
| 2 | 80 | | .Select(propertyInfo => _strainerAttributeProvider.GetPropertyAttribute(propertyInfo)) |
| 4 | 81 | | .FirstOrDefault(attribute => attribute is not null && attribute.IsDefaultSorting); |
| | 82 | |
|
| 2 | 83 | | if (attribute != null) |
| | 84 | | { |
| 2 | 85 | | if (!attribute.IsSortable) |
| | 86 | | { |
| 1 | 87 | | throw new InvalidOperationException( |
| 1 | 88 | | $"Property {attribute.PropertyInfo?.Name ?? attribute.Name} " + |
| 1 | 89 | | $"on {attribute.PropertyInfo?.DeclaringType?.FullName} " + |
| 1 | 90 | | $"is declared as {nameof(IPropertyMetadata.IsDefaultSorting)} " + |
| 1 | 91 | | $"but not as {nameof(IPropertyMetadata.IsSortable)}. " + |
| 1 | 92 | | $"Set the {nameof(IPropertyMetadata.IsSortable)} to true " + |
| 1 | 93 | | $"in order to use the property as a default sortable property."); |
| | 94 | | } |
| | 95 | | } |
| | 96 | |
|
| 1 | 97 | | return attribute; |
| | 98 | | } |
| | 99 | |
|
| | 100 | | public IReadOnlyDictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>> GetMetadataDictionaryFromObjectAttr |
| | 101 | | ICollection<Type> types) |
| | 102 | | { |
| 1 | 103 | | Guard.Against.Null(types); |
| | 104 | |
|
| 1 | 105 | | if (!IsMetadataSourceEnabled(MetadataSourceType.ObjectAttributes)) |
| | 106 | | { |
| 0 | 107 | | return new Dictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>>().ToReadOnly(); |
| | 108 | | } |
| | 109 | |
|
| 1 | 110 | | return types |
| 2 | 111 | | .Select(type => new |
| 2 | 112 | | { |
| 2 | 113 | | Type = type, |
| 2 | 114 | | Attribute = _strainerAttributeProvider.GetObjectAttribute(type), |
| 2 | 115 | | }) |
| 2 | 116 | | .Where(x => x.Attribute != null) |
| 1 | 117 | | .Select(x => new |
| 1 | 118 | | { |
| 1 | 119 | | x.Type, |
| 1 | 120 | | Metadatas = _propertyMetadataDictionaryProvider.GetMetadata(x.Type, x.Attribute!), |
| 1 | 121 | | }) |
| 2 | 122 | | .ToDictionary(x => x.Type, x => x.Metadatas) |
| 1 | 123 | | .ToReadOnly(); |
| | 124 | | } |
| | 125 | |
|
| | 126 | | public IReadOnlyDictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>> GetMetadataDictionaryFromPropertyAt |
| | 127 | | ICollection<Type> types) |
| | 128 | | { |
| 1 | 129 | | Guard.Against.Null(types); |
| | 130 | |
|
| 1 | 131 | | if (!IsMetadataSourceEnabled(MetadataSourceType.ObjectAttributes)) |
| | 132 | | { |
| 0 | 133 | | return new Dictionary<Type, IReadOnlyDictionary<string, IPropertyMetadata>>().ToReadOnly(); |
| | 134 | | } |
| | 135 | |
|
| 1 | 136 | | return types |
| 2 | 137 | | .Select(type => new |
| 2 | 138 | | { |
| 2 | 139 | | Type = type, |
| 2 | 140 | | Attributes = _propertyMetadataDictionaryProvider.GetMetadata(type), |
| 2 | 141 | | }) |
| 2 | 142 | | .Where(x => x.Attributes.Any()) |
| 2 | 143 | | .ToDictionary(x => x.Type, x => x.Attributes) |
| 1 | 144 | | .ToReadOnly(); |
| | 145 | | } |
| | 146 | |
|
| | 147 | | public IPropertyMetadata? GetMetadataFromObjectAttribute( |
| | 148 | | Type modelType, |
| | 149 | | bool isSortableRequired, |
| | 150 | | bool isFilterableRequired, |
| | 151 | | string name) |
| | 152 | | { |
| 0 | 153 | | Guard.Against.Null(modelType); |
| 0 | 154 | | Guard.Against.NullOrWhiteSpace(name); |
| | 155 | |
|
| 0 | 156 | | if (!IsMetadataSourceEnabled(MetadataSourceType.ObjectAttributes)) |
| | 157 | | { |
| 0 | 158 | | return null; |
| | 159 | | } |
| | 160 | |
|
| 0 | 161 | | var currentType = modelType; |
| | 162 | |
|
| | 163 | | do |
| | 164 | | { |
| 0 | 165 | | var attribute = _strainerAttributeProvider.GetObjectAttribute(currentType); |
| 0 | 166 | | var propertyInfo = _propertyInfoProvider.GetPropertyInfo(currentType, name); |
| 0 | 167 | | var isMatching = _attributeCriteriaChecker.CheckIfObjectAttributeIsMatching( |
| 0 | 168 | | attribute, |
| 0 | 169 | | propertyInfo, |
| 0 | 170 | | isSortableRequired, |
| 0 | 171 | | isFilterableRequired); |
| | 172 | |
|
| 0 | 173 | | if (isMatching && attribute is not null && propertyInfo is not null) |
| | 174 | | { |
| 0 | 175 | | return _attributePropertyMetadataBuilder.BuildDefaultMetadataForProperty(attribute, propertyInfo); |
| | 176 | | } |
| | 177 | |
|
| 0 | 178 | | currentType = currentType.BaseType; |
| | 179 | |
|
| | 180 | | } |
| 0 | 181 | | while (currentType != typeof(object) && currentType != typeof(ValueType)); |
| | 182 | |
|
| 0 | 183 | | return null; |
| | 184 | | } |
| | 185 | |
|
| | 186 | | public IPropertyMetadata? GetMetadataFromPropertyAttribute( |
| | 187 | | Type modelType, |
| | 188 | | bool isSortableRequired, |
| | 189 | | bool isFilterableRequired, |
| | 190 | | string name) |
| | 191 | | { |
| 4 | 192 | | Guard.Against.Null(modelType); |
| 4 | 193 | | Guard.Against.NullOrWhiteSpace(name); |
| | 194 | |
|
| 4 | 195 | | if (!IsMetadataSourceEnabled(MetadataSourceType.PropertyAttributes)) |
| | 196 | | { |
| 1 | 197 | | return null; |
| | 198 | | } |
| | 199 | |
|
| 3 | 200 | | var keyValue = _propertyInfoProvider |
| 3 | 201 | | .GetPropertyInfos(modelType) |
| 2 | 202 | | .Select(propertyInfo => new |
| 2 | 203 | | { |
| 2 | 204 | | propertyInfo, |
| 2 | 205 | | attribute = _strainerAttributeProvider.GetPropertyAttribute(propertyInfo), |
| 2 | 206 | | }) |
| 3 | 207 | | .FirstOrDefault(x => |
| 3 | 208 | | { |
| 2 | 209 | | var propertyInfo = x.propertyInfo; |
| 2 | 210 | | var attribute = x.attribute; |
| 3 | 211 | |
|
| 2 | 212 | | return _attributeCriteriaChecker.CheckIfPropertyAttributeIsMatching( |
| 2 | 213 | | attribute, |
| 2 | 214 | | propertyInfo, |
| 2 | 215 | | isSortableRequired, |
| 2 | 216 | | isFilterableRequired, |
| 2 | 217 | | name); |
| 3 | 218 | | }); |
| | 219 | |
|
| 3 | 220 | | return keyValue?.attribute; |
| | 221 | | } |
| | 222 | |
|
| | 223 | | public IReadOnlyList<IPropertyMetadata>? GetMetadataFromObjectAttribute(Type modelType) |
| | 224 | | { |
| 0 | 225 | | Guard.Against.Null(modelType); |
| | 226 | |
|
| 0 | 227 | | if (!IsMetadataSourceEnabled(MetadataSourceType.ObjectAttributes)) |
| | 228 | | { |
| 0 | 229 | | return null; |
| | 230 | | } |
| | 231 | |
|
| 0 | 232 | | var currentType = modelType; |
| | 233 | |
|
| | 234 | | do |
| | 235 | | { |
| 0 | 236 | | var attribute = _strainerAttributeProvider.GetObjectAttribute(currentType); |
| 0 | 237 | | if (attribute != null) |
| | 238 | | { |
| 0 | 239 | | return _propertyInfoProvider.GetPropertyInfos(currentType) |
| 0 | 240 | | .Select(propertyInfo => _attributePropertyMetadataBuilder.BuildDefaultMetadataForProperty(attribute, |
| 0 | 241 | | .ToList() |
| 0 | 242 | | .AsReadOnly(); |
| | 243 | | } |
| | 244 | |
|
| 0 | 245 | | currentType = currentType.BaseType; |
| | 246 | | } |
| 0 | 247 | | while (currentType != typeof(object) && currentType != typeof(ValueType)); |
| | 248 | |
|
| 0 | 249 | | return null; |
| | 250 | | } |
| | 251 | |
|
| | 252 | | public IReadOnlyList<IPropertyMetadata>? GetMetadataFromPropertyAttribute(Type modelType) |
| | 253 | | { |
| 0 | 254 | | Guard.Against.Null(modelType); |
| | 255 | |
|
| 0 | 256 | | if (!IsMetadataSourceEnabled(MetadataSourceType.PropertyAttributes)) |
| | 257 | | { |
| 0 | 258 | | return null; |
| | 259 | | } |
| | 260 | |
|
| 0 | 261 | | var metadata = _propertyInfoProvider |
| 0 | 262 | | .GetPropertyInfos(modelType) |
| 0 | 263 | | .Select(propertyInfo => _strainerAttributeProvider.GetPropertyAttribute(propertyInfo)) |
| 0 | 264 | | .Where(attribute => attribute != null) |
| 0 | 265 | | .Cast<IPropertyMetadata>() |
| 0 | 266 | | .ToList() |
| 0 | 267 | | .AsReadOnly(); |
| | 268 | |
|
| 0 | 269 | | return metadata.Any() |
| 0 | 270 | | ? metadata |
| 0 | 271 | | : null; |
| | 272 | | } |
| | 273 | |
|
| | 274 | | private bool IsMetadataSourceEnabled(MetadataSourceType metadataSourceType) |
| | 275 | | { |
| 12 | 276 | | return _metadataSourceChecker.IsMetadataSourceEnabled(metadataSourceType); |
| | 277 | | } |
| | 278 | | } |