Skip to content

Commit

Permalink
Bugfix - OnChanged method refactored to allow BindingPropertyChangedD…
Browse files Browse the repository at this point in the history
…elegate
  • Loading branch information
rrmanzano committed Apr 14, 2022
1 parent ba73b7b commit 746ce2e
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
using Microsoft.CodeAnalysis.Text;
using System.Text;
using Maui.BindableProperty.Generator.Helpers;
using Maui.BindableProperty.Generator.Core.BindableProperty.Implementation;

namespace Maui.BindableProperty.Generator.Core.BindableProperty
{

[Generator]
public class AutoBindablePropertyGenerator : ISourceGenerator
{
private TypedConstant NameProperty { get; set; }
private readonly List<IImplementation> CustomImplementations = new() { new PropertyChanged() };

private const string attributeText = @"
using System;
Expand Down Expand Up @@ -65,44 +68,71 @@ private void ProcessBindableProperty(CodeWriter w, IFieldSymbol fieldSymbol, ISy
var fieldName = fieldSymbol.Name;
var fieldType = fieldSymbol.Type;

// Get the AutoNotify attribute from the field, and any associated data
var attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
TypedConstant GetValue(string key)
{
return attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == key).Value;
}

var overridenNameProperty = GetValue("PropertyName");
var overridenOnChanged = GetValue("OnChanged");
this.InitializeAttrProperties(fieldSymbol, attributeSymbol, classSymbol);

var propertyName = this.ChooseName(fieldName, overridenNameProperty);
var propertyName = this.ChooseName(fieldName, this.NameProperty);
if (propertyName?.Length == 0 || propertyName == fieldName)
{
// TODO: issue a diagnostic that we can't process this field
return;
}

var bindablePropertyName = $@"{propertyName}Property";
var customParameters = this.ProcessBindableParameters();
w._(AutoBindableConstants.AttributeGeneratedCodeString);
w._($@"public static readonly Microsoft.Maui.Controls.BindableProperty {bindablePropertyName} = Microsoft.Maui.Controls.BindableProperty.Create(nameof({propertyName}), typeof({fieldType}), typeof({classSymbol.Name}), default({fieldType}));");
w._($@"public static readonly Microsoft.Maui.Controls.BindableProperty {bindablePropertyName} = Microsoft.Maui.Controls.BindableProperty.Create(nameof({propertyName}), typeof({fieldType}), typeof({classSymbol.Name}), default({fieldType}){customParameters});");
w._(AutoBindableConstants.AttributeGeneratedCodeString);
using (w.B(@$"public {fieldType} {propertyName}"))
{
w._($@"get => ({fieldType})GetValue({bindablePropertyName});");
if (!overridenOnChanged.IsNull)
if (this.ExistsBodySetter())
{
var method = overridenOnChanged.Value?.ToString();
using (w.B(@$"set"))
{
w._($@"SetValue({bindablePropertyName}, value);",
$@"this.{method}(value);");
w._($@"SetValue({bindablePropertyName}, value);");
this.ProcessBodyStter(w);
}
}
else
{
w._($@"set => SetValue({bindablePropertyName}, value);");
}
}

this.ProcessImplementationLogic(w);
}


private void InitializeAttrProperties(IFieldSymbol fieldSymbol, ISymbol attributeSymbol, INamedTypeSymbol classSymbol)
{
this.NameProperty = fieldSymbol.GetTypedConstant(attributeSymbol, "PropertyName");
this.CustomImplementations.ForEach(i => i.Initialize(this.NameProperty, fieldSymbol, attributeSymbol, classSymbol));
}

private string ProcessBindableParameters()
{
var parameters = this.CustomImplementations
.Select(i => i.ProcessBindableParameters())
.Where(x => !string.IsNullOrEmpty(x));

return parameters.Any() ? $@", { string.Join(", ", parameters) }" : string.Empty;
}

private void ProcessBodyStter(CodeWriter w)
{
this.CustomImplementations
.ForEach(i => i.ProcessBodyStter(w));
}

private void ProcessImplementationLogic(CodeWriter w)
{
this.CustomImplementations
.ForEach(i => i.ProcessImplementationLogic(w));
}

private bool ExistsBodySetter()
{
return this.CustomImplementations.Any(i => i.Implemented());
}

private string ChooseName(string fieldName, TypedConstant overridenNameOpt)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.CodeAnalysis;

namespace Maui.BindableProperty.Generator.Core.BindableProperty.Implementation
{
public interface IImplementation
{
void Initialize(TypedConstant nameProperty, IFieldSymbol fieldSymbol, ISymbol attributeSymbol, INamedTypeSymbol classSymbol);
bool Implemented();
string ProcessBindableParameters();
void ProcessBodyStter(CodeWriter w);
void ProcessImplementationLogic(CodeWriter w);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Maui.BindableProperty.Generator.Helpers;
using Microsoft.CodeAnalysis;

namespace Maui.BindableProperty.Generator.Core.BindableProperty.Implementation
{
public class PropertyChanged : IImplementation
{
private TypedConstant NameProperty { get; set; }
private TypedConstant OnChangedProperty { get; set; }
private IFieldSymbol FieldSymbol { get; set; }
private ISymbol AttributeSymbol { get; set; }
private INamedTypeSymbol ClassSymbol { get; set; }

public void Initialize(TypedConstant nameProperty, IFieldSymbol fieldSymbol, ISymbol attributeSymbol, INamedTypeSymbol classSymbol)
{
this.NameProperty = nameProperty;
this.OnChangedProperty = fieldSymbol.GetTypedConstant(attributeSymbol, "OnChanged");
this.FieldSymbol = fieldSymbol;
this.AttributeSymbol = attributeSymbol;
this.ClassSymbol = classSymbol;
}

public bool Implemented()
{
return !this.OnChangedProperty.IsNull;
}

public string ProcessBindableParameters()
{
if (!this.OnChangedProperty.IsNull)
{
var method = this.OnChangedProperty.Value?.ToString();
return $@"propertyChanged: __{method}";
}

return null;
}

public void ProcessBodyStter(CodeWriter w)
{
if (!this.OnChangedProperty.IsNull)
{
var method = this.OnChangedProperty.Value?.ToString();
w._($@"this.{method}(value);");
}
}

public void ProcessImplementationLogic(CodeWriter w)
{
if (!this.OnChangedProperty.IsNull)
{
var method = this.OnChangedProperty.Value?.ToString();
w._(AutoBindableConstants.AttributeGeneratedCodeString);
using (w.B(@$"public static void __{method}(Microsoft.Maui.Controls.BindableObject bindable, object oldValue, object newValue)"))
{
w._($@"(({this.ClassSymbol.Name})bindable).{method}(({this.FieldSymbol.Type})newValue);");
}
}
}
}
}
17 changes: 17 additions & 0 deletions src/Maui.BindableProperty.Generator/Helpers/SymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.CodeAnalysis;

namespace Maui.BindableProperty.Generator.Helpers
{
internal static class SymbolExtensions
{
public static TypedConstant GetTypedConstant(
this IFieldSymbol fieldSymbol,
ISymbol attributeSymbol,
string key)
{
// Get the AutoNotify attribute from the field, and any associated data
var attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));
return attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == key).Value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Description>Source generator that automatically transforms fields into BindableProperties that can be used in MAUI</Description>
<PackageTags>MAUI;BindableProperty;Source Generator</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
<VersionPrefix>0.4.0</VersionPrefix>
<VersionPrefix>0.4.1</VersionPrefix>
<PackageProjectUrl>https://github.com/rrmanzano/maui-bindableproperty-generator</PackageProjectUrl>
<RepositoryUrl>https://github.com/rrmanzano/maui-bindableproperty-generator</RepositoryUrl>
<PackageId>M.BindableProperty.Generator</PackageId>
Expand Down

0 comments on commit 746ce2e

Please sign in to comment.