From 7bf99a7390bef98a0396be69a46bb011924160c0 Mon Sep 17 00:00:00 2001 From: Tom Pipinic Date: Sun, 18 Dec 2022 08:56:21 +0100 Subject: [PATCH 1/3] Updated references --- src/Connected.Rest/Connected.Rest.csproj | 50 ++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/Connected.Rest/Connected.Rest.csproj b/src/Connected.Rest/Connected.Rest.csproj index 65223e5..c5fb14f 100644 --- a/src/Connected.Rest/Connected.Rest.csproj +++ b/src/Connected.Rest/Connected.Rest.csproj @@ -1,37 +1,39 @@ - - net7.0 - enable - enable - $(MSBuildProjectName.Replace(" ", "_")) - + + net7.0 + enable + enable + $(MSBuildProjectName.Replace(" ", "_")) + + - - - - - + + + + + + - - - True - True - SR.resx - - + + + True + True + SR.resx + + - - - ResXFileCodeGenerator - SR.Designer.cs - - + + + ResXFileCodeGenerator + SR.Designer.cs + + From 3890d27d7f30ad9a39d871f82932c500bda74793 Mon Sep 17 00:00:00 2001 From: Tom Pipinic Date: Mon, 26 Dec 2022 10:14:23 +0100 Subject: [PATCH 2/3] Property aggregation bug fix --- .editorconfig | 4 +- src/Connected.Caching/SR.Designer.cs | 2 +- src/Connected.Collections/SR.Designer.cs | 2 +- src/Connected.Entities/SR.Designer.cs | 2 +- src/Connected.Hosting/SR.Designer.cs | 2 +- src/Connected.Interop/Merging/ObjectMerger.cs | 44 ++---------- .../Merging/PropertyAggregator.cs | 68 +++++++++++++++++++ .../Merging/PropertyResolver.cs | 19 ++++++ src/Connected.Interop/SR.Designer.cs | 2 +- src/Connected.Net/SR.Designer.cs | 2 +- src/Connected.Security/SR.Designer.cs | 2 +- src/Connected.Services/SR.Designer.cs | 2 +- src/Connected.Threading/SR.Designer.cs | 2 +- 13 files changed, 102 insertions(+), 51 deletions(-) create mode 100644 src/Connected.Interop/Merging/PropertyAggregator.cs create mode 100644 src/Connected.Interop/Merging/PropertyResolver.cs diff --git a/.editorconfig b/.editorconfig index fc2933b..8a11f4e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,9 +7,9 @@ root = true #### Core EditorConfig Options #### # Indentation and spacing -indent_size = 4 +indent_size = 3 indent_style = tab -tab_width = 4 +tab_width = 3 # New line preferences end_of_line = crlf diff --git a/src/Connected.Caching/SR.Designer.cs b/src/Connected.Caching/SR.Designer.cs index cedd039..5b6fa9b 100644 --- a/src/Connected.Caching/SR.Designer.cs +++ b/src/Connected.Caching/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Caching { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Caching.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Caching.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Collections/SR.Designer.cs b/src/Connected.Collections/SR.Designer.cs index 0d04761..2503747 100644 --- a/src/Connected.Collections/SR.Designer.cs +++ b/src/Connected.Collections/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Collections { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Collections.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Collections.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Entities/SR.Designer.cs b/src/Connected.Entities/SR.Designer.cs index 5daecd2..0265690 100644 --- a/src/Connected.Entities/SR.Designer.cs +++ b/src/Connected.Entities/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Entities { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Entities.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Entities.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Hosting/SR.Designer.cs b/src/Connected.Hosting/SR.Designer.cs index 15ceb68..d6656c4 100644 --- a/src/Connected.Hosting/SR.Designer.cs +++ b/src/Connected.Hosting/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Hosting { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Hosting.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Hosting.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Interop/Merging/ObjectMerger.cs b/src/Connected.Interop/Merging/ObjectMerger.cs index c00017b..c5a58b7 100644 --- a/src/Connected.Interop/Merging/ObjectMerger.cs +++ b/src/Connected.Interop/Merging/ObjectMerger.cs @@ -1,5 +1,4 @@ -using Connected.ServiceModel; -using System.Collections; +using System.Collections; using System.Reflection; namespace Connected.Interop.Merging @@ -11,7 +10,7 @@ namespace Connected.Interop.Merging if (destination is null || !HasSource(sources)) return; - var sourceProperties = AggregateProperties(sources); + var sourceProperties = PropertyAggregator.Aggregate(sources); foreach (var property in Properties.GetImplementedProperties(destination)) MergeProperty(destination, sourceProperties, property); @@ -28,42 +27,6 @@ namespace Connected.Interop.Merging return false; } - private static Dictionary AggregateProperties(params object[] sources) - { - var result = new Dictionary(); - - for (var i = sources.Length - 1; i >= 0; i--) - { - var source = sources[i]; - - if (source is null) - continue; - - var props = Properties.GetImplementedProperties(source); - - foreach (var property in props) - { - if (result.ContainsKey(property.Name)) - continue; - - result.Add(property.Name, source); - } - - if (source is IPropertyProvider provider) - { - foreach (var property in provider.Properties) - { - if (result.ContainsKey(property.Key)) - continue; - - result.Add(property.Key, property.Value); - } - } - } - - return result; - } - private void MergeProperty(object destination, Dictionary sourceProperties, PropertyInfo property) { if (property.PropertyType.IsTypePrimitive()) @@ -74,7 +37,8 @@ namespace Connected.Interop.Merging if (!sourceProperties.TryGetValue(property.Name, out object? source)) return; - property.SetValue(destination, source.GetType().GetProperty(property.Name).GetValue(source)); + if (source.GetType() is Type propertyType && PropertyResolver.Resolve(propertyType, property.Name) is PropertyInfo propertyInfo) + property.SetValue(destination, propertyInfo.GetValue(source)); } else if (IsArray(property)) MergeEnumerable(destination, sourceProperties, property); diff --git a/src/Connected.Interop/Merging/PropertyAggregator.cs b/src/Connected.Interop/Merging/PropertyAggregator.cs new file mode 100644 index 0000000..052fbc2 --- /dev/null +++ b/src/Connected.Interop/Merging/PropertyAggregator.cs @@ -0,0 +1,68 @@ +using Connected.ServiceModel; + +namespace Connected.Interop.Merging; +internal static class PropertyAggregator +{ + public static Dictionary Aggregate(params object[] values) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + for (var i = values.Length - 1; i >= 0; i--) + { + if (values[i] is not object value) + continue; + + foreach (var property in GetImplementedProperties(value)) + { + if (result.ContainsKey(property.Key)) + continue; + + result.Add(property.Key, property.Value); + } + + if (value is IPropertyProvider provider) + { + foreach (var property in provider.Properties) + { + if (result.ContainsKey(property.Key)) + continue; + + result.Add(property.Key, property.Value); + } + } + } + + return result; + } + + private static Dictionary GetImplementedProperties(object value) + { + if (value is null) + return new(); + + if (value is object[] objectArray) + { + var result = new Dictionary(); + + for (var i = objectArray.Length - 1; i >= 0; i--) + { + var implementations = Properties.GetImplementedProperties(objectArray[i]); + + foreach (var property in implementations) + result.Add(property.Name, objectArray[i]); + } + + return result; + } + else + { + var implementations = Properties.GetImplementedProperties(value); + var result = new Dictionary(); + + foreach (var property in implementations) + result.Add(property.Name, value); + + return result; + } + } +} diff --git a/src/Connected.Interop/Merging/PropertyResolver.cs b/src/Connected.Interop/Merging/PropertyResolver.cs new file mode 100644 index 0000000..ba32fdc --- /dev/null +++ b/src/Connected.Interop/Merging/PropertyResolver.cs @@ -0,0 +1,19 @@ +using System.Reflection; + +namespace Connected.Interop.Merging; +internal static class PropertyResolver +{ + public static PropertyInfo? Resolve(Type type, string propertyName) + { + if (type.GetProperty(propertyName) is PropertyInfo property) + return property; + + if (type.GetProperty(propertyName.ToCamelCase()) is PropertyInfo camelProperty) + return camelProperty; + + if (type.GetProperty(propertyName.ToPascalCase()) is PropertyInfo pascalProperty) + return pascalProperty; + + return null; + } +} diff --git a/src/Connected.Interop/SR.Designer.cs b/src/Connected.Interop/SR.Designer.cs index 5d78d99..8c29b86 100644 --- a/src/Connected.Interop/SR.Designer.cs +++ b/src/Connected.Interop/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Interop { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Interop.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Interop.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Net/SR.Designer.cs b/src/Connected.Net/SR.Designer.cs index 0a5df82..7c56800 100644 --- a/src/Connected.Net/SR.Designer.cs +++ b/src/Connected.Net/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Net { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Net.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Net.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Security/SR.Designer.cs b/src/Connected.Security/SR.Designer.cs index 4c62e88..387c024 100644 --- a/src/Connected.Security/SR.Designer.cs +++ b/src/Connected.Security/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Security { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Security.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Security.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Services/SR.Designer.cs b/src/Connected.Services/SR.Designer.cs index 757bc43..ccc3baf 100644 --- a/src/Connected.Services/SR.Designer.cs +++ b/src/Connected.Services/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Services { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Services.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Services.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Connected.Threading/SR.Designer.cs b/src/Connected.Threading/SR.Designer.cs index 9a1d472..0399ab3 100644 --- a/src/Connected.Threading/SR.Designer.cs +++ b/src/Connected.Threading/SR.Designer.cs @@ -39,7 +39,7 @@ namespace Connected.Threading { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Server.Threading.SR", typeof(SR).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Connected.Threading.SR", typeof(SR).Assembly); resourceMan = temp; } return resourceMan; From 43312c336245ac9f3f549aa332b7155148198bd4 Mon Sep 17 00:00:00 2001 From: Tom Pipinic Date: Tue, 7 Feb 2023 10:54:27 +0100 Subject: [PATCH 3/3] AsUrlFriendly extension added --- src/Connected.Services/ServicesExtensions.cs | 10 ++ src/Connected.Services/UrlGenerator.cs | 112 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/Connected.Services/UrlGenerator.cs diff --git a/src/Connected.Services/ServicesExtensions.cs b/src/Connected.Services/ServicesExtensions.cs index a84f6b9..4941bca 100644 --- a/src/Connected.Services/ServicesExtensions.cs +++ b/src/Connected.Services/ServicesExtensions.cs @@ -51,4 +51,14 @@ public static class ServicesExtensions return (f is not null && type.GetInterface(f) is not null) || (nf is not null && type.GetInterface(nf) is not null); } + + public static string AsUrlFriendly(this string value) + { + return UrlGenerator.Generate(value); + } + + public static string AsUrlFriendly(this string value, IEnumerable existing) + { + return UrlGenerator.Generate(value, existing); + } } \ No newline at end of file diff --git a/src/Connected.Services/UrlGenerator.cs b/src/Connected.Services/UrlGenerator.cs new file mode 100644 index 0000000..ce6a527 --- /dev/null +++ b/src/Connected.Services/UrlGenerator.cs @@ -0,0 +1,112 @@ +using System.Text; + +namespace Connected.Services; +internal static class UrlGenerator +{ + private const string ValidCharacters = "abcdefghijklmnopqrstuvzxyw-"; + private const string MinusReplacements = "_ .\t\r\n"; + static UrlGenerator() + { + Replacements = new(StringComparer.OrdinalIgnoreCase) + { + {"č", "c"}, + {"š", "s"}, + {"ž", "z"}, + {"đ", "d"}, + {"ć", "c"} + }; + } + + private static Dictionary Replacements { get; } + + public static string Generate(string text) + { + if (string.IsNullOrEmpty(text)) + throw new NullReferenceException(nameof(text)); + + var result = new StringBuilder(); + /* + * Analyze each character. + */ + foreach (var s in text.Trim()) + { + /* + * If it's in a ValidCharacters collection that's fine, we just copy it + * into the result. + */ + if (ValidCharacters.Contains(s, StringComparison.OrdinalIgnoreCase)) + { + result.Append(s); + continue; + } + /* + * Look if it's in the MinusReplacements collection which means we'll + * replace the character with the minus sign. + */ + if (MinusReplacements.Contains(s, StringComparison.OrdinalIgnoreCase)) + { + result.Append('-'); + continue; + } + /* + * Last chance for the character to be included in the result is to + * resolve the hardcoded character replacements. + */ + if (Replacements.TryGetValue(s.ToString(), out var replacement)) + result.Append(replacement); + } + /* + * It's possible that no characters are included in the result. If that's + * the case we are going to include an a character just for the sake of + * being a valid string. + */ + if (result.Length == 0) + result.Append('a'); + /* + * Remove any redundant minus characters and we are done. + */ + var sb = new StringBuilder(); + var active = false; + + for (var i = 0; i < result.Length; i++) + { + if (result[i] == '-') + { + if (active) + continue; + + active = true; + sb.Append(result[i]); + } + else + { + active = false; + sb.Append(result[i]); + } + } + /* + * Remove trailing minus if exists. + */ + return sb.ToString().Trim().Trim('-').ToLowerInvariant(); + } + + public static string Generate(string text, IEnumerable existing) + { + var prepared = Generate(text); + + if (!existing.Any()) + return prepared; + + var idx = 0; + + while (true) + { + var key = $"{prepared}-{idx}"; + + if (!existing.Contains(key, StringComparer.OrdinalIgnoreCase)) + return key; + + idx++; + } + } +} \ No newline at end of file