@@ -26,6 +26,8 @@ protected class BuilderGen : FeatureGenerator
2626 private static readonly IdentifierNameSyntax ToImmutableMethodName = SyntaxFactory . IdentifierName ( "ToImmutable" ) ;
2727 private static readonly IdentifierNameSyntax CreateBuilderMethodName = SyntaxFactory . IdentifierName ( "CreateBuilder" ) ;
2828 private static readonly IdentifierNameSyntax ImmutableFieldName = SyntaxFactory . IdentifierName ( "immutable" ) ;
29+ private static readonly TypeSyntax INotifyPropertyChanged = SyntaxFactory . ParseTypeName ( "System.ComponentModel.INotifyPropertyChanged" ) ;
30+ private static readonly IdentifierNameSyntax OnPropertyChangedMethodName = SyntaxFactory . IdentifierName ( "OnPropertyChanged" ) ;
2931
3032 public BuilderGen ( CodeGen generator )
3133 : base ( generator )
@@ -55,8 +57,7 @@ protected override void GenerateCore()
5557 var builderType = SyntaxFactory . ClassDeclaration ( BuilderTypeName . Identifier )
5658 . AddModifiers (
5759 SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) ,
58- SyntaxFactory . Token ( SyntaxKind . PartialKeyword ) )
59- . WithMembers ( SyntaxFactory . List ( builderMembers ) ) ;
60+ SyntaxFactory . Token ( SyntaxKind . PartialKeyword ) ) ;
6061 if ( this . generator . applyToMetaType . HasAncestor )
6162 {
6263 builderType = builderType
@@ -66,6 +67,16 @@ protected override void GenerateCore()
6667 BuilderTypeName ) ) ) ) )
6768 . WithModifiers ( builderType . Modifiers . Insert ( 0 , SyntaxFactory . Token ( SyntaxKind . NewKeyword ) ) ) ;
6869 }
70+ else
71+ {
72+ builderType = builderType
73+ . AddBaseListTypes ( SyntaxFactory . SimpleBaseType ( INotifyPropertyChanged ) ) ;
74+ builderMembers . Add ( this . CreatePropertyChangedEvent ( ) ) ;
75+ builderMembers . Add ( this . CreateOnPropertyChangedMethod ( ) ) ;
76+ }
77+
78+ builderType = builderType
79+ . WithMembers ( SyntaxFactory . List ( builderMembers ) ) ;
6980
7081 this . innerMembers . Add ( builderType ) ;
7182 }
@@ -182,11 +193,12 @@ protected IReadOnlyList<MemberDeclarationSyntax> CreateMutableProperties()
182193 foreach ( var field in this . generator . applyToMetaType . LocalFields )
183194 {
184195 var thisField = Syntax . ThisDot ( field . NameAsField ) ;
196+ var optionalFieldNotYetDefined = SyntaxFactory . PrefixUnaryExpression ( SyntaxKind . LogicalNotExpression , Syntax . OptionalIsDefined ( thisField ) ) ;
185197 var getterBlock = field . IsGeneratedImmutableType
186198 ? SyntaxFactory . Block (
187199 // if (!this.fieldName.IsDefined) {
188200 SyntaxFactory . IfStatement (
189- SyntaxFactory . PrefixUnaryExpression ( SyntaxKind . LogicalNotExpression , Syntax . OptionalIsDefined ( thisField ) ) ,
201+ optionalFieldNotYetDefined ,
190202 SyntaxFactory . Block (
191203 // this.fieldName = this.immutable.fieldName?.ToBuilder();
192204 SyntaxFactory . ExpressionStatement ( SyntaxFactory . AssignmentExpression (
@@ -202,11 +214,38 @@ protected IReadOnlyList<MemberDeclarationSyntax> CreateMutableProperties()
202214 SyntaxFactory . ArgumentList ( ) ) ) ) ) ) ) ,
203215 SyntaxFactory . ReturnStatement ( Syntax . OptionalValue ( thisField ) ) )
204216 : SyntaxFactory . Block ( SyntaxFactory . ReturnStatement ( thisField ) ) ;
205- var setterBlock = SyntaxFactory . Block (
217+ var setterValueArg = SyntaxFactory . IdentifierName ( "value" ) ;
218+ var setterCondition = field . IsGeneratedImmutableType ?
219+ SyntaxFactory . BinaryExpression (
220+ SyntaxKind . LogicalOrExpression ,
221+ optionalFieldNotYetDefined ,
222+ SyntaxFactory . BinaryExpression (
223+ SyntaxKind . NotEqualsExpression ,
224+ Syntax . OptionalValue ( thisField ) ,
225+ setterValueArg ) ) :
226+ HasEqualityOperators ( field . Symbol . Type ) ?
227+ SyntaxFactory . BinaryExpression (
228+ SyntaxKind . NotEqualsExpression ,
229+ thisField ,
230+ setterValueArg ) :
231+ null ;
232+ var setterSignificantBlock = SyntaxFactory . Block (
206233 SyntaxFactory . ExpressionStatement ( SyntaxFactory . AssignmentExpression (
207234 SyntaxKind . SimpleAssignmentExpression ,
208235 thisField ,
209- SyntaxFactory . IdentifierName ( "value" ) ) ) ) ;
236+ setterValueArg ) ) ,
237+ SyntaxFactory . ExpressionStatement (
238+ SyntaxFactory . InvocationExpression (
239+ SyntaxFactory . MemberAccessExpression (
240+ SyntaxKind . SimpleMemberAccessExpression ,
241+ SyntaxFactory . ThisExpression ( ) ,
242+ OnPropertyChangedMethodName ) ) ) ) ;
243+ var setterBlock = setterCondition != null ?
244+ SyntaxFactory . Block (
245+ SyntaxFactory . IfStatement (
246+ setterCondition ,
247+ setterSignificantBlock ) ) :
248+ setterSignificantBlock ;
210249
211250 var property = SyntaxFactory . PropertyDeclaration (
212251 this . GetPropertyTypeForBuilder ( field ) ,
@@ -239,6 +278,51 @@ protected NameSyntax GetFieldTypeForBuilder(MetaField field)
239278 : typeBasis ;
240279 }
241280
281+ protected EventFieldDeclarationSyntax CreatePropertyChangedEvent ( )
282+ {
283+ var handler = SyntaxFactory . ParseTypeName ( "System.ComponentModel.PropertyChangedEventHandler" ) ;
284+ return SyntaxFactory . EventFieldDeclaration (
285+ SyntaxFactory . VariableDeclaration ( handler )
286+ . AddVariables ( SyntaxFactory . VariableDeclarator ( nameof ( System . ComponentModel . INotifyPropertyChanged . PropertyChanged ) ) ) )
287+ . WithModifiers ( SyntaxFactory . TokenList ( SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) ) ) ;
288+ }
289+
290+ protected MethodDeclarationSyntax CreateOnPropertyChangedMethod ( )
291+ {
292+ var callerMemberName = SyntaxFactory . ParseName ( "System.Runtime.CompilerServices.CallerMemberNameAttribute" ) ;
293+ var propertyNameParameterName = SyntaxFactory . IdentifierName ( "propertyName" ) ;
294+ var evt = SyntaxFactory . MemberAccessExpression (
295+ SyntaxKind . SimpleMemberAccessExpression ,
296+ SyntaxFactory . ThisExpression ( ) ,
297+ SyntaxFactory . IdentifierName ( nameof ( System . ComponentModel . INotifyPropertyChanged . PropertyChanged ) ) ) ;
298+ var invokeMethod = SyntaxFactory . ConditionalAccessExpression (
299+ evt ,
300+ SyntaxFactory . InvocationExpression (
301+ SyntaxFactory . MemberBindingExpression ( SyntaxFactory . IdentifierName ( nameof ( System . ComponentModel . PropertyChangedEventHandler . Invoke ) ) ) ,
302+ SyntaxFactory . ArgumentList ( ) . AddArguments (
303+ SyntaxFactory . Argument ( SyntaxFactory . ThisExpression ( ) ) ,
304+ SyntaxFactory . Argument (
305+ SyntaxFactory . ObjectCreationExpression (
306+ SyntaxFactory . ParseTypeName ( "System.ComponentModel.PropertyChangedEventArgs" ) ,
307+ SyntaxFactory . ArgumentList ( ) . AddArguments (
308+ SyntaxFactory . Argument ( propertyNameParameterName ) ) ,
309+ null ) ) ) ) ) ;
310+ var body = SyntaxFactory . Block (
311+ SyntaxFactory . ExpressionStatement ( invokeMethod ) ) ;
312+ return SyntaxFactory . MethodDeclaration (
313+ SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . VoidKeyword ) ) ,
314+ OnPropertyChangedMethodName . Identifier )
315+ . AddParameterListParameters (
316+ SyntaxFactory . Parameter ( propertyNameParameterName . Identifier )
317+ . WithType ( SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . StringKeyword ) ) )
318+ . AddAttributeLists ( SyntaxFactory . AttributeList ( ) . AddAttributes ( SyntaxFactory . Attribute ( callerMemberName ) ) )
319+ . WithDefault ( SyntaxFactory . EqualsValueClause ( SyntaxFactory . LiteralExpression ( SyntaxKind . StringLiteralExpression , SyntaxFactory . Literal ( string . Empty ) ) ) ) )
320+ . AddModifiers (
321+ SyntaxFactory . Token ( SyntaxKind . ProtectedKeyword ) ,
322+ SyntaxFactory . Token ( SyntaxKind . VirtualKeyword ) )
323+ . WithBody ( body ) ;
324+ }
325+
242326 protected MethodDeclarationSyntax CreateToImmutableMethod ( )
243327 {
244328 // var fieldName = this.fieldName.IsDefined ? this.fieldName.Value?.ToImmutable() : this.immutable.FieldName;
0 commit comments