CodeGen
ModelClassGenerator.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Codeastysis;
using Microsoft.Codeastysis.CSharp;
using Microsoft.Codeastysis.CSharp.Syntax;
using SqExpress.CodeGenUtil.Model.SqModel;
using SqExpress.QueryBuilders.RecordSetter;
using SqExpress.Syntax.Names;
using static SqExpress.CodeGenUtil.CodeGen.SyntaxHelpers;
namespace SqExpress.CodeGenUtil.CodeGen
{
internal clast ModelClastGenerator
{
private const string MethodNameGetColumns = "GetColumns";
private const string MethodNameGetMapping = "GetMapping";
private const string MethodNameGetUpdateKeyMapping = "GetUpdateKeyMapping";
private const string MethodNameGetUpdateMapping = "GetUpdateMapping";
private const string MethodNameRead = "Read";
private const string MethodNameReadOrdinal = "ReadOrdinal";
private const string ReaderClastSuffix = "Reader";
private const string MethodNameGetReader = "GetReader";
private const string UpdaterClastSuffix = "Updater";
private const string MethodNameGetUpdater = "GetUpdater";
private static readonly HashSet AllMethods = new HashSet
{
MethodNameGetColumns,
MethodNameGetMapping,
MethodNameGetUpdateKeyMapping,
MethodNameGetUpdateMapping,
MethodNameRead,
MethodNameReadOrdinal,
MethodNameGetReader,
MethodNameGetUpdater
};
public static CompilationUnitSyntax Generate(SqModelMeta meta, string defaultNamespace, string existingFilePath, bool rwClastes, IFileSystem fileSystem, out bool existing)
{
CompilationUnitSyntax result;
ClastDeclarationSyntax? existingClast = null;
existing = false;
if (fileSystem.FileExists(existingFilePath))
{
existing = true;
var tClast = CSharpSyntaxTree.ParseText(fileSystem.ReadAllText(existingFilePath));
existingClast = tClast.GetRoot()
.DescendantNodes()
.OfType()
.FirstOrDefault(cd => cd.Identifier.ValueText == meta.Name);
}
var namespaces =
new[] {
nameof(System),
nameof(SqExpress),
$"{nameof(SqExpress)}.{nameof(SqExpress.QueryBuilders)}.{nameof(SqExpress.QueryBuilders.RecordSetter)}"
}
.Concat(meta.Properties.SelectMany(p => p.Column)
.Select(c => c.TableRef.TableTypeNameSpace)
.Where(n => n != defaultNamespace))
.Distinct()
.ToList();
if (rwClastes || ExtractTableRefs(meta).Any(tr => tr.BaseTypeKindTag == BaseTypeKindTag.DerivedTableBase))
{
namespaces.Add($"{nameof(SqExpress)}.{nameof(SqExpress.Syntax)}.{nameof(SqExpress.Syntax.Names)}");
namespaces.Add($"{nameof(System)}.{nameof(System.Collections)}.{nameof(System.Collections.Generic)}");
}
if (existingClast != null)
{
result = existingClast.FindParentOrDefault() ?? throw new SqExpressCodeGenException($"Could not find compilation unit in \"{existingFilePath}\"");
foreach (var usingDirectiveSyntax in result.Usings)
{
var existingUsing = usingDirectiveSyntax.Name.ToFullString();
var index = namespaces.IndexOf(existingUsing);
if (index >= 0)
{
namespaces.RemoveAt(index);
}
}
if (namespaces.Count > 0)
{
result = result.AddUsings(namespaces
.Select(n => SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(n)))
.ToArray());
}
result = result.ReplaceNode(existingClast, GenerateClast(meta, rwClastes, existingClast));
}
else
{
result = SyntaxFactory.CompilationUnit()
.AddUsings(namespaces.Select(n => SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(n))).ToArray())
.AddMembers(SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(defaultNamespace))
.AddMembers(GenerateClast(meta, rwClastes, null)));
}
return result.NormalizeWhitespace();
}
private static ClastDeclarationSyntax GenerateClast(SqModelMeta meta, bool rwClastes, ClastDeclarationSyntax? existingClast)
{
ClastDeclarationSyntax result;
MemberDeclarationSyntax[]? oldMembers = null;
Dictionary? oldAttributes = null;
if (existingClast != null)
{
result = existingClast;
oldMembers = result.Members
.Where(md =>
{
if (md is ConstructorDeclarationSyntax)
{
return false;
}
if (md is IncompleteMemberSyntax)
{
return false;
}
if (md is PropertyDeclarationSyntax p)
{
if (meta.Properties.Any(mp => mp.Name == p.Identifier.ValueText))
{
if (p.AttributeLists.Count > 0)
{
oldAttributes ??= new Dictionary();
oldAttributes.Add(p.Identifier.ValueText, p.AttributeLists);
}
return false;
}
}
if (md is MethodDeclarationSyntax method)
{
var name = method.Identifier.ValueText;
if (name.StartsWith("With") || AllMethods.Contains(name) || name.StartsWith(MethodNameGetReader + "For") || name.StartsWith(MethodNameGetUpdater + "For"))
{
return false;
}
}
if (md is ClastDeclarationSyntax clastDeclaration)
{
var name = clastDeclaration.Identifier.ValueText;
if (name == meta.Name + ReaderClastSuffix || name.StartsWith(meta.Name + ReaderClastSuffix + "For"))
{
return false;
}
if (name == meta.Name + UpdaterClastSuffix || name.StartsWith(meta.Name + UpdaterClastSuffix + "For"))
{
return false;
}
}
return true;
})
.ToArray();
result = result.RemoveNodes(result.DescendantNodes().OfType(), SyntaxRemoveOptions.KeepNoTrivia)!;
}
else
{
result = SyntaxFactory.ClastDeclaration(meta.Name)
.WithModifiers(existingClast?.Modifiers ?? Modifiers(SyntaxKind.PublicKeyword));
}
result = result
.AddMembers(Constructors(meta)
.Concat(GenerateStaticFactory(meta))
.Concat(rwClastes ? GenerateOrdinalStaticFactory(meta) : Array.Empty())
.Concat(Properties(meta, oldAttributes))
.Concat(GenerateWithModifiers(meta))
.Concat(GenerateGetColumns(meta))
.Concat(GenerateMapping(meta))
.Concat(rwClastes ? GenerateReaderClast(meta): Array.Empty())
.Concat(rwClastes ? GenerateWriterClast(meta) : Array.Empty())
.ToArray());
if (oldMembers != null && oldMembers.Length > 0)
{
result = result.AddMembers(oldMembers);
}
return result;
}
public static IEnumerable Properties(SqModelMeta meta, IReadOnlyDictionary? oldAttributes)
{
return meta.Properties.Select(p =>
{
var res = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.ParseTypeName(p.FinalType),
p.Name)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
);
if (oldAttributes != null && oldAttributes.TryGetValue(p.Name, out var attributeList))
{
res = res.WithAttributeLists(attributeList);
}
return res;
});
}
public static MemberDeclarationSyntax[] Constructors(SqModelMeta meta)
{
var constructor = SyntaxFactory.ConstructorDeclaration(meta.Name)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword))
.AddParameterListParameters(meta.Properties.Select(p=> FuncParameter(p.Name.FirstToLower(), p.FinalType)).ToArray())
.WithBody(SyntaxFactory.Block(GenerateConstructorastignments(meta)));
return new MemberDeclarationSyntax[] {constructor};
}
private static IEnumerable GenerateConstructorastignments(SqModelMeta meta)
{
return meta.Properties.Select(p =>
SyntaxFactory.ExpressionStatement(astignmentThis(p.Name,
SyntaxFactory.IdentifierName(p.Name.FirstToLower()))));
}
public static IEnumerable GenerateStaticFactory(SqModelMeta meta)
{
return ExtractTableRefs(meta).Select(tableRef => SyntaxFactory
.MethodDeclaration(SyntaxFactory.ParseTypeName(meta.Name), MethodNameRead)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
.AddParameterListParameters(FuncParameter("record", nameof(ISqDataRecordReader)))
.AddParameterListParameters(FuncParameter("table", ExtractTableTypeName(meta, tableRef)))
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(
SyntaxFactory.ObjectCreationExpression(SyntaxFactory.Token(SyntaxKind.NewKeyword),
SyntaxFactory.ParseTypeName(meta.Name),
ArgumentList(meta.Properties.Select(p =>
{
ExpressionSyntax invocation = MemberAccess("table", p.Column.First().ColumnName)
.MemberAccess("Read")
.Invoke(SyntaxFactory.ParseName("record"));
if (p.CastType != null)
{
invocation = SyntaxFactory.CastExpression(SyntaxFactory.ParseTypeName(p.CastType), invocation);
}
return new NamedArgument(p.Name.FirstToLower(),
invocation);
})
.ToArray()),
null)))));
}
public static IEnumerable GenerateOrdinalStaticFactory(SqModelMeta meta)
{
return ExtractTableRefs(meta).Select(tableRef => SyntaxFactory
.MethodDeclaration(SyntaxFactory.ParseTypeName(meta.Name), MethodNameReadOrdinal)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
.AddParameterListParameters(FuncParameter("record", nameof(ISqDataRecordReader)))
.AddParameterListParameters(FuncParameter("table", ExtractTableTypeName(meta, tableRef)))
.AddParameterListParameters(FuncParameter("offset", "int"))
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(
SyntaxFactory.ObjectCreationExpression(SyntaxFactory.Token(SyntaxKind.NewKeyword),
SyntaxFactory.ParseTypeName(meta.Name),
ArgumentList(meta.Properties.Select((p,index) =>
{
ExpressionSyntax invocation = MemberAccess("table", p.Column.First().ColumnName)
.MemberAccess("Read")
.Invoke(
SyntaxFactory.ParseName("record"),
index == 0
? SyntaxFactory.IdentifierName("offset")
: SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression, SyntaxFactory.IdentifierName("offset"), SyntaxFactory.LiteralExpression(SyntaxKind.NumerireplacederalExpression, SyntaxFactory.Literal(index))));
if (p.CastType != null)
{
invocation = SyntaxFactory.CastExpression(SyntaxFactory.ParseTypeName(p.CastType), invocation);
}
return new NamedArgument(p.Name.FirstToLower(),
invocation);
})
.ToArray()),
null)))));
}
public static IEnumerable GenerateGetColumns(SqModelMeta meta)
{
return ExtractTableRefs(meta).Select(tableRef =>
{
string columnTypeName = ExtractTableColumnTypeName(tableRef);
var arrayItems = meta.Properties.Select(p => p.Column.First())
.Select(p => SyntaxFactory.IdentifierName("table").MemberAccess(p.ColumnName));
var arrayType = SyntaxFactory.ArrayType(
SyntaxFactory.IdentifierName(columnTypeName),
new SyntaxList(new[]
{
SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.Token(SyntaxKind.OpenBracketToken),
new SeparatedSyntaxList(),
SyntaxFactory.Token(SyntaxKind.CloseBracketToken))
}));
var array = SyntaxFactory.ArrayCreationExpression(
arrayType,
SyntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression,
new SeparatedSyntaxList().AddRange(arrayItems))
);
return SyntaxFactory
.MethodDeclaration(arrayType, MethodNameGetColumns)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
.AddParameterListParameters(FuncParameter("table", ExtractTableTypeName(meta, tableRef)))
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(
array
)));
});
}
public static IEnumerable GenerateMapping(SqModelMeta meta)
{
return ExtractTableRefs(meta).SelectMany(tr => GenerateMapping(meta, tr));
}
public static MemberDeclarationSyntax[] GenerateMapping(SqModelMeta meta, SqModelTableRef tableRef)
{
if (!HasUpdater(tableRef))
{
return Array.Empty();
}
if (meta.HasPk())
{
return new []
{
MethodDeclarationSyntax(meta, tableRef, MethodNameGetMapping, null),
MethodDeclarationSyntax(meta, tableRef,MethodNameGetUpdateKeyMapping, true),
MethodDeclarationSyntax(meta, tableRef,MethodNameGetUpdateMapping, false)
};
}
else
{
return new [] { MethodDeclarationSyntax(meta, tableRef, MethodNameGetMapping, null) };
}
static MemberDeclarationSyntax MethodDeclarationSyntax(SqModelMeta sqModelMeta, SqModelTableRef tableRef, string name, bool? pkFilter)
{
var setter = SyntaxFactory.IdentifierName("s");
ExpressionSyntax chain = setter;
foreach (var metaProperty in sqModelMeta.Properties.Where(p => pkFilter.HasValue? p.IsPrimaryKey == pkFilter.Value : !p.IsIdensaty))
{
var col = setter.MemberAccess(nameof(IDataMapSetter.Target))
.MemberAccess(metaProperty.Column.First(c=>c.TableRef.Equals(tableRef)).ColumnName);
ExpressionSyntax prop = setter.MemberAccess(nameof(IDataMapSetter.Source))
.MemberAccess(metaProperty.Name);
if (metaProperty.CastType != null)
{
prop = SyntaxFactory.CastExpression(SyntaxFactory.ParseTypeName(metaProperty.Type), prop);
}
chain = chain.MemberAccess("Set").Invoke(col, prop);
}
var methodDeclarationSyntax = SyntaxFactory
.MethodDeclaration(SyntaxFactory.ParseTypeName(nameof(IRecordSetterNext)), name)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
.AddParameterListParameters(FuncParameter("s",
$"{nameof(IDataMapSetter)}"))
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(chain)));
return methodDeclarationSyntax;
}
}
public static IEnumerable GenerateWithModifiers(SqModelMeta meta)
{
return meta.Properties.Select(p =>
{
var args = meta.Properties.Select(subP =>
new NamedArgument(subP.Name.FirstToLower(),
p == subP
? SyntaxFactory.IdentifierName(subP.Name.FirstToLower())
: MemberAccessThis(subP.Name)
))
.ToArray();
return SyntaxFactory
.MethodDeclaration(SyntaxFactory.ParseTypeName(meta.Name), $"With{p.Name}")
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword))
.AddParameterListParameters(FuncParameter(p.Name.FirstToLower(), p.FinalType))
.WithBody(SyntaxFactory.Block(SyntaxFactory.ReturnStatement(
SyntaxFactory.ObjectCreationExpression(SyntaxFactory.Token(SyntaxKind.NewKeyword),
SyntaxFactory.ParseTypeName(meta.Name),
ArgumentList(args),
initializer: null)
)));
});
}
public static IEnumerable GenerateReaderClast(SqModelMeta meta)
{
return ExtractTableRefs(meta, out var addName).SelectMany(tableRef => GenerateReaderClast(meta, tableRef, addName));
}
public static IEnumerable GenerateReaderClast(SqModelMeta meta, SqModelTableRef tableRef, bool addName)
{
string tableType = ExtractTableTypeName(meta, tableRef);
var clastName = meta.Name + ReaderClastSuffix;
if (addName)
{
clastName += $"For{tableRef.TableTypeName}";
}
var clastType = SyntaxFactory.ParseTypeName(clastName);
var baseInterface = SyntaxFactory.GenericName(
SyntaxFactory.Identifier(nameof(ISqModelReader)))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.IdentifierName(tableType)
})));
//Instance
var instance = SyntaxFactory.PropertyDeclaration(
clastType,
SyntaxFactory.Identifier("Instance"))
.WithModifiers(
SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.SingletonList(
SyntaxFactory.AccessorDeclaration(
SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken)))))
.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(clastType)
.WithArgumentList(SyntaxFactory.ArgumentList())))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken));
//GetColumns
var getColumns = SyntaxFactory.MethodDeclaration(
SyntaxFactory.GenericName(
SyntaxFactory.Identifier(nameof(IReadOnlyList)))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.IdentifierName(nameof(ExprColumn))
}))),
SyntaxFactory.Identifier(MethodNameGetColumns))
.WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(baseInterface))
.WithParameterList(
SyntaxFactory.ParameterList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("table"))
.WithType(
SyntaxFactory.IdentifierName(tableType)))))
.WithBody(
SyntaxFactory.Block(
SyntaxFactory.SingletonList(
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.IdentifierName(nameof(ISqModelReader.GetColumns))))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("table")))))))));
//Read
var read = SyntaxFactory.MethodDeclaration(
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.Identifier(MethodNameRead))
.WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(baseInterface))
.WithParameterList(
SyntaxFactory.ParameterList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("record"))
.WithType(
SyntaxFactory.IdentifierName(nameof(ISqDataRecordReader))),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("table"))
.WithType(
SyntaxFactory.IdentifierName(tableType))
})))
.WithBody(
SyntaxFactory.Block(
SyntaxFactory.SingletonList(
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.IdentifierName(MethodNameRead)))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("record")),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("table"))
})))))));
//ReadOrdinal
var readOrdinal = SyntaxFactory.MethodDeclaration(
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.Identifier(MethodNameReadOrdinal))
.WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(baseInterface))
.WithParameterList(
SyntaxFactory.ParameterList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("record"))
.WithType(
SyntaxFactory.IdentifierName(nameof(ISqDataRecordReader))),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("table"))
.WithType(
SyntaxFactory.IdentifierName(tableType)), SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("offset"))
.WithType(
SyntaxFactory.IdentifierName("int"))
})))
.WithBody(
SyntaxFactory.Block(
SyntaxFactory.SingletonList(
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.IdentifierName(MethodNameReadOrdinal)))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("record")),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("table")),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName("offset"))
})))))));
var readerClastDeclaration = SyntaxFactory.ClastDeclaration(clastName)
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)))
.WithBaseList(SyntaxFactory.BaseList().AddTypes(SyntaxFactory.SimpleBaseType(baseInterface)))
.AddMembers(instance, getColumns, read, readOrdinal);
var getReader = SyntaxFactory.MethodDeclaration(baseInterface, addName ? $"{MethodNameGetReader}For{tableRef.TableTypeName}" : MethodNameGetReader)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword))
.AddBodyStatements(SyntaxFactory.ReturnStatement(MemberAccess(clastName, "Instance")));
return new MemberDeclarationSyntax[] {getReader, readerClastDeclaration};
}
public static IEnumerable GenerateWriterClast(SqModelMeta meta)
{
return ExtractTableRefs(meta, out var addName).SelectMany(tableRef => GenerateWriterClast(meta, tableRef, addName));
}
public static IEnumerable GenerateWriterClast(SqModelMeta meta, SqModelTableRef tableRef, bool addName)
{
if (!HasUpdater(tableRef))
{
return Array.Empty();
}
var tableType = ExtractTableTypeName(meta, tableRef);
var clastName = meta.Name + UpdaterClastSuffix;
if (addName)
{
clastName += $"For{tableRef.TableTypeName}";
}
var clastType = SyntaxFactory.ParseTypeName(clastName);
bool hasPk = meta.HasPk();
var baseInterfaceKeyLess = SyntaxFactory.GenericName(
SyntaxFactory.Identifier(nameof(ISqModelUpdater)))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.IdentifierName(tableType)
})));
var baseInterfaceKey = SyntaxFactory.GenericName(
SyntaxFactory.Identifier(nameof(ISqModelUpdaterKey)))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]
{
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.IdentifierName(tableType)
})));
var baseInterface = hasPk ? baseInterfaceKey : baseInterfaceKeyLess;
//Instance
var instance = SyntaxFactory.PropertyDeclaration(
clastType,
SyntaxFactory.Identifier("Instance"))
.WithModifiers(
SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
.WithAccessorList(
SyntaxFactory.AccessorList(
SyntaxFactory.SingletonList(
SyntaxFactory.AccessorDeclaration(
SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken)))))
.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(clastType)
.WithArgumentList(SyntaxFactory.ArgumentList())))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken));
//GetMapping
var dataMapSetterName = "dataMapSetter";
var parameterDataMapperSetter = SyntaxFactory.Parameter(
SyntaxFactory.Identifier(dataMapSetterName))
.WithType(
SyntaxFactory.GenericName(
SyntaxFactory.Identifier(nameof(IDataMapSetter)))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
new SyntaxNodeOrToken[]{
SyntaxFactory.IdentifierName(tableType),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.IdentifierName(meta.Name)}))));
var updaterClastDeclaration = SyntaxFactory.ClastDeclaration(clastName)
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)))
.WithBaseList(SyntaxFactory.BaseList().AddTypes(SyntaxFactory.SimpleBaseType(baseInterface)))
.AddMembers(
instance,
GetMapping(baseInterfaceKeyLess, MethodNameGetMapping));
if (hasPk)
{
updaterClastDeclaration = updaterClastDeclaration.AddMembers(
GetMapping(baseInterfaceKey, MethodNameGetUpdateKeyMapping),
GetMapping(baseInterfaceKey, MethodNameGetUpdateMapping));
}
MethodDeclarationSyntax GetMapping(GenericNameSyntax bi, string s)
{
return SyntaxFactory.MethodDeclaration(SyntaxFactory.IdentifierName(nameof(IRecordSetterNext)),
SyntaxFactory.Identifier(s))
.WithExplicitInterfaceSpecifier(SyntaxFactory.ExplicitInterfaceSpecifier(bi))
.WithParameterList(
SyntaxFactory.ParameterList(SyntaxFactory.SingletonSeparatedList(parameterDataMapperSetter)))
.WithBody(
SyntaxFactory.Block(
SyntaxFactory.SingletonList(
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(meta.Name),
SyntaxFactory.IdentifierName(s)))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName(dataMapSetterName)))))))));
}
var getUpdater = SyntaxFactory.MethodDeclaration(baseInterface, addName ? $"{MethodNameGetUpdater}For{tableRef.TableTypeName}" : MethodNameGetUpdater)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword))
.AddBodyStatements(SyntaxFactory.ReturnStatement(MemberAccess(clastName, "Instance")));
return new MemberDeclarationSyntax[] { getUpdater, updaterClastDeclaration };
}
private static string ExtractTableTypeName(SqModelMeta meta, SqModelTableRef tableRef)
{
string tableType = tableRef.TableTypeName;
if (tableType == meta.Name)
{
tableType = $"{tableRef.TableTypeNameSpace}.{tableType}";
}
return tableType;
}
private static IEnumerable ExtractTableRefs(SqModelMeta meta)
=> ExtractTableRefs(meta, out _);
private static IEnumerable ExtractTableRefs(SqModelMeta meta, out bool multi)
{
var first = meta.Properties.First();
multi = first.Column.Count > 1;
return first.Column.Select(c => c.TableRef);
}
private static string ExtractTableColumnTypeName(SqModelTableRef tableRef)
=> tableRef.BaseTypeKindTag.Switch(
tableBaseRes: nameof(TableColumn),
tempTableBaseRes: nameof(TableColumn),
derivedTableBaseRes: nameof(ExprColumn));
private static bool HasUpdater(SqModelTableRef tableRef)
=> tableRef.BaseTypeKindTag.Switch(
tableBaseRes: true,
tempTableBaseRes: true,
derivedTableBaseRes: false);
}
}