private void PopulateDictionary(BuilderState state, PropertyInformation propertyInformation) { var propertyName = propertyInformation.Property.Name; state.AppendLine($"if({propertyName} != null)"); state.AppendLine("{"); var ifBody = state.IncrementIdentation(); ifBody.AppendLine($"foreach(var item in {propertyName})"); ifBody.AppendLine("{"); var foreachBody = ifBody.IncrementIdentation(); foreachBody.AppendLine("if(item.Value is null)"); foreachBody.IncrementIdentation().AppendLine($"input.{propertyName}.Remove(item.Key);"); foreachBody.AppendLine("else"); if (propertyInformation.IsConvertedToNullableType) { foreachBody.IncrementIdentation().AppendLine($"input.{propertyName}[item.Key] = item.Value.Value;"); } else { foreachBody.IncrementIdentation().AppendLine($"input.{propertyName}[item.Key] = item.Value;"); } ifBody.AppendLine("}"); state.AppendLine("}"); }
private void SetReadWriteProperties(BuilderState state) { for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { var currentProperty = state.TypeInfo.Properties[i].Property; if (!IsInitOnlyProperty(currentProperty)) { state.AppendLine($"if (Properties[{i}])"); if (GeneratedTypeFilter.IsGeneratableType(currentProperty.Type)) { state.IncrementIdentation().AppendLine($"input.{currentProperty.Name} = {currentProperty.Name}?.ApplyPatch(input.{currentProperty.Name});"); } else if (state.TypeInfo.Properties[i].IsGenericDictionary) { state.IncrementIdentation().AppendLine($"input.{currentProperty.Name} ??= new();"); } else if (state.TypeInfo.Properties[i].IsConvertedToNullableType) { state.IncrementIdentation().AppendLine($"input.{currentProperty.Name} = {currentProperty.Name}.HasValue ? {currentProperty.Name}.Value : default;"); } else { state.IncrementIdentation().AppendLine($"input.{currentProperty.Name} = {currentProperty.Name};"); } } } }
private void BuildClassBody(BuilderState state) { BuildConstructor(state); state.AppendLine(); BuildAllProperties(state); BuildApplyPath(state); }
private void BuildClassDeclaration(BuilderState state, Action <BuilderState>?addBody = null) { BuildAttributes(state, state.TypeInfo.TypeSymbol.GetAttributes()); state.AppendLine($"public class {state.TypeInfo.Name} : LaDeak.JsonMergePatch.Abstractions.Patch<{state.TypeInfo.SourceTypeName}>"); state.AppendLine("{"); addBody?.Invoke(state.IncrementIdentation()); state.AppendLine("}"); }
private void BuildAllProperties(BuilderState state) { for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { BuildPropery(state, state.TypeInfo.Properties[i], i); state.AppendLine(); } }
private void BuildNamespace(BuilderState state, Action <BuilderState>?addBody = null) { state.AppendLine($"#nullable enable"); state.AppendLine($"namespace {NameBuilder.GetNamespace(state.TypeInfo.TypeSymbol)}"); state.AppendLine("{"); addBody?.Invoke(state.IncrementIdentation()); state.AppendLine("}"); state.AppendLine($"#nullable disable"); }
private void BuildConstructor(BuilderState state, Action <BuilderState>?addBody = null) { state.AppendLine($"public {state.TypeInfo.Name}()"); state.AppendLine("{"); var innerState = state.IncrementIdentation(); innerState.AppendLine($"Properties = new bool[{state.TypeInfo.Properties.Count}];"); addBody?.Invoke(innerState); state.AppendLine("}"); }
private void PopulateDictionaryProperties(BuilderState state) { for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { var currentProperty = state.TypeInfo.Properties[i].Property; if (!GeneratedTypeFilter.IsGeneratableType(currentProperty.Type) && state.TypeInfo.Properties[i].IsGenericDictionary) { PopulateDictionary(state, state.TypeInfo.Properties[i]); } } }
private void BuildAttributes(BuilderState state, IEnumerable <AttributeData> attributes) { foreach (var attribute in attributes) { if (attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableContextAttribute" && attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableAttribute" && attribute.AttributeClass?.ToDisplayString() != "LaDeak.JsonMergePatch.Abstractions.PatchableAttribute") { BuildAttribute(state, attribute); } } }
private void BuildApplyPath(BuilderState state) { state.AppendLine($"public override {state.TypeInfo.SourceTypeName} ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull] {state.TypeInfo.SourceTypeName} input)"); state.AppendLine("{"); var bodyState = state.IncrementIdentation(); CallConstructIfEmpty(bodyState, "input ??=", leaveOpen: false); SetInitOnlyProperties(bodyState); SetReadWriteProperties(bodyState); PopulateDictionaryProperties(bodyState); bodyState.AppendLine("return input;"); state.AppendLine("}"); }
private void CallConstructIfEmpty(BuilderState state, string toInitialize, bool leaveOpen) { IEnumerable <string> parameters = Enumerable.Empty <string>(); if (!HasDefaultConstructor(state.TypeInfo.TypeSymbol)) { var typeSymbol = state.TypeInfo.TypeSymbol; var ctor = typeSymbol.GetMembers().OfType <IMethodSymbol>().Where(x => x.MethodKind == MethodKind.Constructor).OrderByDescending(x => x.Parameters.Length).First(); var properties = state.TypeInfo.Properties.Select(x => x.Property).ToList(); parameters = Enumerable.Repeat("default", ctor.Parameters.Length); } var ending = leaveOpen ? "" : ";"; state.AppendLine($"{toInitialize} new {state.TypeInfo.SourceTypeName}({string.Join(", ", parameters)}){ending}"); return; }
private void SetInitOnlyProperties(BuilderState state) { if (!state.TypeInfo.Properties.Any(x => IsInitOnlyProperty(x.Property))) { return; } CallConstructIfEmpty(state, "var tmp =", leaveOpen: true); state.AppendLine("{"); var initializerState = state.IncrementIdentation(); for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { var currentProperty = state.TypeInfo.Properties[i].Property; if (IsInitOnlyProperty(currentProperty)) { if (GeneratedTypeFilter.IsGeneratableType(currentProperty.Type)) { initializerState.AppendLine($"{currentProperty.Name} = Properties[{i}] ? this.{currentProperty.Name}?.ApplyPatch(input.{currentProperty.Name}) : input.{currentProperty.Name},"); } else if (state.TypeInfo.Properties[i].IsGenericDictionary) { initializerState.AppendLine($"{currentProperty.Name} = Properties[{i}] && input.{currentProperty.Name} == null ? new() : input.Values,"); } else if (state.TypeInfo.Properties[i].IsConvertedToNullableType) { initializerState.AppendLine($"{currentProperty.Name} = Properties[{i}] ? ({currentProperty.Name}.HasValue ? this.{currentProperty.Name}.Value : default) : input.{currentProperty.Name},"); } else { initializerState.AppendLine($"{currentProperty.Name} = Properties[{i}] ? this.{currentProperty.Name} : input.{currentProperty.Name},"); } } else { // Copy old property values onto the new object initializerState.AppendLine($"{currentProperty.Name} = input.{currentProperty.Name},"); } } state.AppendLine("};"); state.AppendLine("input = tmp;"); }
private void BuildPropery(BuilderState state, PropertyInformation propertyInfo, int propertyId) { state.ToProcessTypeSymbols.Add(propertyInfo.Property.Type); string fieldName = Casing.PrefixUnderscoreCamelCase(propertyInfo.Property.Name); string propertyTypeName = GetPropertyTypeName(propertyInfo); state.AppendLine($"private {propertyTypeName} {fieldName};"); BuildAttributes(state, propertyInfo.Property.GetAttributes()); state.AppendLine($"public {propertyTypeName} {propertyInfo.Property.Name}"); state.AppendLine("{"); var getterSetter = state.IncrementIdentation(); getterSetter.AppendLine($"get {{ return {fieldName}; }}"); getterSetter.AppendLine("set"); getterSetter.AppendLine("{"); var setterBody = getterSetter.IncrementIdentation(); setterBody.AppendLine($"Properties[{propertyId}] = true;"); setterBody.AppendLine($"{fieldName} = value;"); getterSetter.AppendLine("}"); state.AppendLine("}"); }
private void BuildClass(BuilderState state) => BuildClassDeclaration(state, s => BuildClassBody(s));
private void BuildFile(BuilderState state) => BuildNamespace(state, BuildClass);
private void BuildAttribute(BuilderState state, AttributeData attribute) => state.AppendLine($"[{attribute}]");