Skip to content

Commit 5397088

Browse files
committed
Added DefaultStringComparison to ComplexValueObjectAttribute
1 parent d9f30d1 commit 5397088

15 files changed

+1089
-11
lines changed

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/AllValueObjectSettings.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public sealed class AllValueObjectSettings : IEquatable<AllValueObjectSettings>,
2727
public ConversionOperatorsGeneration ConversionFromKeyMemberType { get; }
2828
public ConversionOperatorsGeneration UnsafeConversionToKeyMemberType { get; }
2929
public ConversionOperatorsGeneration ConversionToKeyMemberType { get; }
30+
public StringComparison DefaultStringComparison { get; }
3031

3132
public AllValueObjectSettings(AttributeData valueObjectAttribute)
3233
{
@@ -55,6 +56,7 @@ public AllValueObjectSettings(AttributeData valueObjectAttribute)
5556
ConversionToKeyMemberType = valueObjectAttribute.FindConversionToKeyMemberType() ?? ConversionOperatorsGeneration.Implicit;
5657
UnsafeConversionToKeyMemberType = valueObjectAttribute.FindUnsafeConversionToKeyMemberType() ?? ConversionOperatorsGeneration.Explicit;
5758
ConversionFromKeyMemberType = valueObjectAttribute.FindConversionFromKeyMemberType() ?? ConversionOperatorsGeneration.Explicit;
59+
DefaultStringComparison = valueObjectAttribute.FindDefaultStringComparison();
5860

5961
// Comparison operators depend on the equality comparison operators
6062
if (ComparisonOperators > EqualityComparisonOperators)
@@ -97,7 +99,8 @@ public bool Equals(AllValueObjectSettings? other)
9799
&& AllowDefaultStructs == other.AllowDefaultStructs
98100
&& ConversionToKeyMemberType == other.ConversionToKeyMemberType
99101
&& UnsafeConversionToKeyMemberType == other.UnsafeConversionToKeyMemberType
100-
&& ConversionFromKeyMemberType == other.ConversionFromKeyMemberType;
102+
&& ConversionFromKeyMemberType == other.ConversionFromKeyMemberType
103+
&& DefaultStringComparison == other.DefaultStringComparison;
101104
}
102105

103106
public override int GetHashCode()
@@ -129,6 +132,7 @@ public override int GetHashCode()
129132
hashCode = (hashCode * 397) ^ (int)ConversionToKeyMemberType;
130133
hashCode = (hashCode * 397) ^ (int)UnsafeConversionToKeyMemberType;
131134
hashCode = (hashCode * 397) ^ (int)ConversionFromKeyMemberType;
135+
hashCode = (hashCode * 397) ^ (int)DefaultStringComparison;
132136

133137
return hashCode;
134138
}

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ComplexValueObjectCodeGenerator.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ public bool Equals(").AppendTypeFullyQualifiedNullAnnotated(_state).Append(@" ot
454454
}
455455
else if (member.IsString())
456456
{
457-
_sb.Append("global::System.StringComparer.OrdinalIgnoreCase.Equals(this.").Append(member.Name).Append(", other.").Append(member.Name).Append(")");
457+
_sb.Append("global::System.StringComparer.").Append(GetDefaultStringComparer()).Append(".Equals(this.").Append(member.Name).Append(", other.").Append(member.Name).Append(")");
458458
}
459459
else
460460
{
@@ -481,6 +481,21 @@ public bool Equals(").AppendTypeFullyQualifiedNullAnnotated(_state).Append(@" ot
481481
}");
482482
}
483483

484+
private string GetDefaultStringComparer()
485+
{
486+
var comparer = _state.Settings.DefaultStringComparison switch
487+
{
488+
StringComparison.Ordinal => "Ordinal",
489+
StringComparison.OrdinalIgnoreCase => "OrdinalIgnoreCase",
490+
StringComparison.CurrentCulture => "CurrentCulture",
491+
StringComparison.CurrentCultureIgnoreCase => "CurrentCultureIgnoreCase",
492+
StringComparison.InvariantCulture => "InvariantCulture",
493+
StringComparison.InvariantCultureIgnoreCase => "InvariantCultureIgnoreCase",
494+
_ => "OrdinalIgnoreCase"
495+
};
496+
return comparer;
497+
}
498+
484499
private void GenerateGetHashCode()
485500
{
486501
_sb.Append(@"
@@ -491,7 +506,7 @@ public override int GetHashCode()
491506

492507
if (_state.EqualityMembers.Count > 0)
493508
{
494-
var useShortForm = _state.EqualityMembers.Count < 8 && _state.EqualityMembers.All(m => m.EqualityComparerAccessor == null);
509+
var useShortForm = _state.EqualityMembers.Count < 8 && _state.EqualityMembers.All(m => m.EqualityComparerAccessor == null && !m.Member.IsString());
495510

496511
if (useShortForm)
497512
{
@@ -529,7 +544,7 @@ public override int GetHashCode()
529544
}
530545
else if (member.IsString())
531546
{
532-
_sb.Append(", global::System.StringComparer.OrdinalIgnoreCase");
547+
_sb.Append(", global::System.StringComparer.").Append(GetDefaultStringComparer());
533548

534549
if (member is { IsReferenceType: true, NullableAnnotation: NullableAnnotation.Annotated })
535550
_sb.Append("!");

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/ValueObjects/ValueObjectSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace Thinktecture.CodeAnalysis.ValueObjects;
1818
public ConversionOperatorsGeneration ConversionToKeyMemberType => _allSettings.ConversionToKeyMemberType;
1919
public ConversionOperatorsGeneration UnsafeConversionToKeyMemberType => _allSettings.UnsafeConversionToKeyMemberType;
2020
public ConversionOperatorsGeneration ConversionFromKeyMemberType => _allSettings.ConversionFromKeyMemberType;
21+
public StringComparison DefaultStringComparison => _allSettings.DefaultStringComparison;
2122
public bool HasStructLayoutAttribute => _attributeInfo.HasStructLayoutAttribute;
2223
public string? KeyMemberEqualityComparerAccessor => _attributeInfo.KeyMemberEqualityComparerAccessor;
2324
public ImmutableArray<DesiredFactory> DesiredFactories => _attributeInfo.DesiredFactories;
@@ -45,6 +46,7 @@ public bool Equals(ValueObjectSettings other)
4546
&& ConversionToKeyMemberType == other.ConversionToKeyMemberType
4647
&& UnsafeConversionToKeyMemberType == other.UnsafeConversionToKeyMemberType
4748
&& ConversionFromKeyMemberType == other.ConversionFromKeyMemberType
49+
&& DefaultStringComparison == other.DefaultStringComparison
4850
&& HasStructLayoutAttribute == other.HasStructLayoutAttribute
4951
&& KeyMemberEqualityComparerAccessor == other.KeyMemberEqualityComparerAccessor
5052
&& DesiredFactories.SequenceEqual(other.DesiredFactories);
@@ -72,6 +74,7 @@ public override int GetHashCode()
7274
hashCode = (hashCode * 397) ^ (int)ConversionToKeyMemberType;
7375
hashCode = (hashCode * 397) ^ (int)UnsafeConversionToKeyMemberType;
7476
hashCode = (hashCode * 397) ^ (int)ConversionFromKeyMemberType;
77+
hashCode = (hashCode * 397) ^ (int)DefaultStringComparison;
7578
hashCode = (hashCode * 397) ^ HasStructLayoutAttribute.GetHashCode();
7679
hashCode = (hashCode * 397) ^ (KeyMemberEqualityComparerAccessor?.GetHashCode() ?? 0);
7780
hashCode = (hashCode * 397) ^ DesiredFactories.ComputeHashCode();

src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/AttributeDataExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ public static SerializationFrameworks FindUseForSerialization(this AttributeData
166166

167167
public static StringComparison FindDefaultStringComparison(this AttributeData attributeData)
168168
{
169-
var frameworks = (StringComparison?)GetIntegerParameterValue(attributeData, "DefaultStringComparison");
169+
var defaultStringComparison = (StringComparison?)GetIntegerParameterValue(attributeData, "DefaultStringComparison");
170170

171-
if (frameworks is null || !frameworks.Value.IsValid())
171+
if (defaultStringComparison is null || !defaultStringComparison.Value.IsValid())
172172
return StringComparison.OrdinalIgnoreCase;
173173

174-
return frameworks.Value;
174+
return defaultStringComparison.Value;
175175
}
176176

177177
public static bool FindTxIsNullableReferenceType(this AttributeData attributeData, int index)

src/Thinktecture.Runtime.Extensions/ComplexValueObjectAttribute.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@ namespace Thinktecture;
66
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
77
public sealed class ComplexValueObjectAttribute : ValueObjectAttributeBase
88
{
9+
/// <summary>
10+
/// Defines the <see cref="StringComparison"/>.
11+
/// Default <see cref="StringComparison"/> is <see cref="StringComparison.OrdinalIgnoreCase"/>.
12+
/// </summary>
13+
public StringComparison DefaultStringComparison { get; set; } = StringComparison.OrdinalIgnoreCase;
914
}

test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/Snapshots/ValueObjectSourceGeneratorTests.Should_generate_complex_class_with_nullable_members.verified.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,11 @@ public bool Equals(global::Thinktecture.Tests.TestValueObject? other)
168168
/// <inheritdoc />
169169
public override int GetHashCode()
170170
{
171-
return global::System.HashCode.Combine(
172-
_typeHashCode,
173-
this.Prop1,
174-
this.Prop2);
171+
var hashCode = new global::System.HashCode();
172+
hashCode.Add(_typeHashCode);
173+
hashCode.Add(this.Prop1, global::System.StringComparer.OrdinalIgnoreCase!);
174+
hashCode.Add(this.Prop2);
175+
return hashCode.ToHashCode();
175176
}
176177

177178
/// <inheritdoc />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// <auto-generated />
2+
#nullable enable
3+
4+
namespace Thinktecture.Tests
5+
{
6+
sealed partial class TestValueObject : global::System.IEquatable<global::Thinktecture.Tests.TestValueObject?>,
7+
global::System.Numerics.IEqualityOperators<global::Thinktecture.Tests.TestValueObject, global::Thinktecture.Tests.TestValueObject, bool>,
8+
global::Thinktecture.IComplexValueObject
9+
{
10+
[global::System.Runtime.CompilerServices.ModuleInitializer]
11+
internal static void ModuleInit()
12+
{
13+
global::System.Linq.Expressions.Expression<global::System.Func<TestValueObject, object>> action = o => new
14+
{
15+
o.Property
16+
};
17+
18+
var members = new global::System.Collections.Generic.List<global::System.Reflection.MemberInfo>();
19+
20+
foreach (var arg in ((global::System.Linq.Expressions.NewExpression)action.Body).Arguments)
21+
{
22+
members.Add(((global::System.Linq.Expressions.MemberExpression)arg).Member);
23+
}
24+
25+
var type = typeof(global::Thinktecture.Tests.TestValueObject);
26+
var metadata = new global::Thinktecture.Internal.ComplexValueObjectMetadata(type, members.AsReadOnly());
27+
28+
global::Thinktecture.Internal.ComplexValueObjectMetadataLookup.AddMetadata(type, metadata);
29+
}
30+
31+
private static readonly int _typeHashCode = typeof(global::Thinktecture.Tests.TestValueObject).GetHashCode();
32+
33+
public static global::Thinktecture.ValidationError? Validate(
34+
string @property,
35+
out global::Thinktecture.Tests.TestValueObject? obj)
36+
{
37+
global::Thinktecture.ValidationError? validationError = null;
38+
ValidateFactoryArguments(
39+
ref validationError,
40+
ref @property);
41+
42+
if (validationError is null)
43+
{
44+
obj = new global::Thinktecture.Tests.TestValueObject(
45+
@property);
46+
obj.FactoryPostInit();
47+
}
48+
else
49+
{
50+
obj = default;
51+
}
52+
53+
return validationError;
54+
}
55+
56+
public static global::Thinktecture.Tests.TestValueObject Create(
57+
string @property)
58+
{
59+
var validationError = Validate(
60+
@property,
61+
out global::Thinktecture.Tests.TestValueObject? obj);
62+
63+
if (validationError is not null)
64+
throw new global::System.ComponentModel.DataAnnotations.ValidationException(validationError.ToString() ?? "Validation failed.");
65+
66+
return obj!;
67+
}
68+
69+
public static bool TryCreate(
70+
string @property,
71+
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Thinktecture.Tests.TestValueObject? obj)
72+
{
73+
return TryCreate(
74+
@property,
75+
out obj,
76+
out _);
77+
}
78+
79+
public static bool TryCreate(
80+
string @property,
81+
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Thinktecture.Tests.TestValueObject? obj,
82+
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(false)] out global::Thinktecture.ValidationError? validationError)
83+
{
84+
validationError = Validate(
85+
@property,
86+
out obj);
87+
88+
return validationError is null;
89+
}
90+
91+
static partial void ValidateFactoryArguments(
92+
ref global::Thinktecture.ValidationError? validationError,
93+
[global::System.Diagnostics.CodeAnalysis.AllowNullAttribute, global::System.Diagnostics.CodeAnalysis.NotNullAttribute] ref string @property);
94+
95+
partial void FactoryPostInit();
96+
97+
private TestValueObject(
98+
string @property)
99+
{
100+
ValidateConstructorArguments(
101+
ref @property);
102+
103+
this.Property = @property;
104+
}
105+
106+
static partial void ValidateConstructorArguments(
107+
ref string @property);
108+
109+
/// <summary>
110+
/// Compares two instances of <see cref="TestValueObject"/>.
111+
/// </summary>
112+
/// <param name="obj">Instance to compare.</param>
113+
/// <param name="other">Another instance to compare.</param>
114+
/// <returns><c>true</c> if objects are equal; otherwise <c>false</c>.</returns>
115+
public static bool operator ==(global::Thinktecture.Tests.TestValueObject? obj, global::Thinktecture.Tests.TestValueObject? other)
116+
{
117+
if (obj is null)
118+
return other is null;
119+
120+
return obj.Equals(other);
121+
}
122+
123+
/// <summary>
124+
/// Compares two instances of <see cref="TestValueObject"/>.
125+
/// </summary>
126+
/// <param name="obj">Instance to compare.</param>
127+
/// <param name="other">Another instance to compare.</param>
128+
/// <returns><c>false</c> if objects are equal; otherwise <c>true</c>.</returns>
129+
public static bool operator !=(global::Thinktecture.Tests.TestValueObject? obj, global::Thinktecture.Tests.TestValueObject? other)
130+
{
131+
return !(obj == other);
132+
}
133+
134+
/// <inheritdoc />
135+
public override bool Equals(object? other)
136+
{
137+
return other is global::Thinktecture.Tests.TestValueObject obj && Equals(obj);
138+
}
139+
140+
/// <inheritdoc />
141+
public bool Equals(global::Thinktecture.Tests.TestValueObject? other)
142+
{
143+
if (other is null)
144+
return false;
145+
146+
if (global::System.Object.ReferenceEquals(this, other))
147+
return true;
148+
149+
return global::System.StringComparer.CurrentCulture.Equals(this.Property, other.Property);
150+
}
151+
152+
/// <inheritdoc />
153+
public override int GetHashCode()
154+
{
155+
var hashCode = new global::System.HashCode();
156+
hashCode.Add(_typeHashCode);
157+
hashCode.Add(this.Property, global::System.StringComparer.CurrentCulture);
158+
return hashCode.ToHashCode();
159+
}
160+
161+
/// <inheritdoc />
162+
public override string ToString()
163+
{
164+
return $"{{ Property = {this.Property} }}";
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)