public static void AddUnions(OutputModel outputModel, OutputPlan outputPlan) { foreach (var databasePlan in outputPlan.DatabasePlansByName.Values) { foreach (var partitionPlan in databasePlan.PartitionPlansByName.Values) { foreach (var union in partitionPlan.Documents.GroupBy(x => x.GetIdPlan.FullMethodName).Where(x => x.Count() > 1)) { var idPlan = union.First().GetIdPlan; var fullTypeName = idPlan.FullMethodName.Substring(0, idPlan.FullMethodName.LastIndexOf('.')); var commonName = fullTypeName.Split('.').Last(); if (commonName.EndsWith("Base")) { commonName = commonName.Substring(0, commonName.Length - 4); } if (commonName.EndsWith("Doc")) { commonName = commonName.Substring(0, commonName.Length - 3); } partitionPlan.Unions.Add(new UnionPlan { GetIdPlan = idPlan, Documents = union.ToList(), FullCommonTypeName = fullTypeName, CommonName = commonName }); } } } }
public static OutputPlan Create(OutputModel outputModel) { outputModel.CancellationToken.ThrowIfCancellationRequested(); var plan = new OutputPlan(); DatabasePlanBuilder.Build(outputModel, plan); return(plan); }
public static void AddPartitions(OutputModel outputModel, OutputPlan outputPlan) { outputModel.CancellationToken.ThrowIfCancellationRequested(); if (!outputModel.CanGenerate) { return; } AddPartitionClasses(outputModel, outputPlan); AddImplicitPartitions(outputModel, outputPlan); }
public static void Write(OutputModel outputModel, OutputPlan outputPlan) { outputModel.CancellationToken.ThrowIfCancellationRequested(); if (!outputModel.CanGenerate) { return; } foreach (var databasePlan in outputPlan.DatabasePlansByName.Values) { DatabasePlanWriter.Write(outputModel, databasePlan); } }
public static void Build(OutputModel outputModel, OutputPlan outputPlan) { outputModel.CancellationToken.ThrowIfCancellationRequested(); if (!outputModel.CanGenerate) { return; } var databasePlansByClass = new Dictionary <ClassModel, List <DatabasePlan> >(); foreach (var dbAttribute in outputModel.DbAttributes) { Initialize(outputModel, null, outputPlan, dbAttribute); } foreach (var classModel in outputModel.Classes) { outputPlan.DatabasePlansByClass[classModel] = new(); foreach (var dbAttribute in classModel.DbAttributes) { Initialize(outputModel, classModel, outputPlan, dbAttribute); } } var defaultDatabase = outputPlan.DatabasePlansByName.Count == 1 ? outputPlan.DatabasePlansByName.Values.Single() : null; if (defaultDatabase is not null) { foreach (var byClass in outputPlan.DatabasePlansByClass.Values.Where(x => x.Count == 0)) { byClass.Add(defaultDatabase); } } foreach (var classModel in outputPlan.DatabasePlansByName.Values.GroupBy(x => x.Namespace).Where(x => x.Count() > 1).Select(x => x.First().ClassModel)) { outputModel.Report(Diagnostics.Errors.DatabaseNamespaces, classModel?.ClassSymbol as ISymbol ?? outputModel.Compilation.Assembly); } PartitionPlanBuilder.AddPartitions(outputModel, outputPlan); DocumentPlanBuilder.AddDocuments(outputModel, outputPlan); PartitionPlanBuilder.RemoveEmptyPartitions(outputModel, outputPlan); UnionPlanBuilder.AddUnions(outputModel, outputPlan); foreach (var databasePlan in outputPlan.DatabasePlansByName.Values) { foreach (var documentPlan in databasePlan.PartitionPlansByName.Values.SelectMany(x => x.Documents).GroupBy(x => x.DocType).Where(x => x.Count() > 1).Select(x => x.First())) { outputModel.Report(Diagnostics.Errors.DuplicateDocType, documentPlan.ClassModel.ClassSymbol, documentPlan.DocType); } } }
/// <summary> /// Exports the plan under a XML format. /// </summary> /// <param name="planToExport"></param> /// <returns></returns> public static string ExportAsXML(Plan plan) { // Generates a settings plan and transforms it to an output plan var serial = plan.Export(); var output = new OutputPlan { Name = serial.Name, Owner = serial.Owner, Revision = Settings.Revision }; output.Entries.AddRange(serial.Entries); // Serializes to XML document and gets a string representation var doc = Util.SerializeToXmlDocument(typeof(OutputPlan), output); return(Util.GetXMLStringRepresentation(doc)); }
public static void RemoveEmptyPartitions(OutputModel outputModel, OutputPlan outputPlan) { outputModel.CancellationToken.ThrowIfCancellationRequested(); if (!outputModel.CanGenerate) { return; } foreach (var databasePlan in outputPlan.DatabasePlansByName.Values) { foreach (var partitionPlan in databasePlan.PartitionPlansByName.Values.Where(x => x.Documents.Count == 0).ToList()) { databasePlan.PartitionPlansByName.Remove(partitionPlan.Name); outputModel.Report(Diagnostics.Warnings.EmptyPartition, partitionPlan.GetPkModel.MethodSymbol, partitionPlan.Name); } } }
/// <summary> /// Exports the plan under an XML format. /// </summary> /// <param name="plan">The plan.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">plan</exception> public static string ExportAsXML(Plan plan) { plan.ThrowIfNull(nameof(plan)); // Generates a settings plan and transforms it to an output plan SerializablePlan serial = plan.Export(); OutputPlan output = new OutputPlan { Name = serial.Name, Owner = serial.Owner, Revision = Settings.Revision }; output.Entries.AddRange(serial.Entries); // Serializes to XML document and gets a string representation XmlDocument doc = (XmlDocument)Util.SerializeToXmlDocument(output); return(Util.GetXmlStringRepresentation(doc)); }
static void AddPartitionClasses(OutputModel outputModel, OutputPlan outputPlan) { foreach (var kvp in outputPlan.DatabasePlansByClass) { var classModel = kvp.Key; var databasePlans = kvp.Value; if (classModel.PartitionDefinitionAttribute is not null) { if (databasePlans.Count == 0) { outputModel.Report(Diagnostics.Errors.NoDatabase, classModel.ClassSymbol); } foreach (var methodModel in classModel.Methods) { var name = methodModel.MethodSymbol.Name; foreach (var databasePlan in databasePlans) { AddPartitionDefinition(outputModel, databasePlan, methodModel, name); } } } } }
static void AddImplicitPartitions(OutputModel outputModel, OutputPlan outputPlan) { foreach (var kvp in outputPlan.DatabasePlansByClass) { var classModel = kvp.Key; var databasePlans = kvp.Value; if (classModel.PartitionAttribute is not null) { var name = classModel.PartitionAttribute.Name; foreach (var databasePlan in databasePlans) { if (!databasePlan.PartitionPlansByName.ContainsKey(name)) { var getPk = classModel.Methods .Where(x => x.MethodSymbol.Name == "GetPk") .Where(x => x.MethodSymbol.IsStatic) .Where(x => x.MethodSymbol.ReturnType.SpecialType == SpecialType.System_String) .Where(x => x.MethodSymbol.DeclaredAccessibility.IsAccessible()) .ToList(); if (getPk.Count == 0) { outputModel.Report(Diagnostics.Errors.NoGetPk, classModel.ClassSymbol); } else if (getPk.Count > 1) { outputModel.Report(Diagnostics.Errors.MultipleGetPk, classModel.ClassSymbol); } else { AddPartitionDefinition(outputModel, databasePlan, getPk[0], name); } } } } } }
static void Initialize(OutputModel outputModel, ClassModel?classModel, OutputPlan outputPlan, DbAttributeModel dbAttribute) { var name = dbAttribute.Name ?? ""; var symbol = classModel?.ClassSymbol as ISymbol ?? outputModel.Compilation.Assembly; if (outputPlan.DatabasePlansByName.TryGetValue(name, out var plan)) { if (dbAttribute.Namespace.NullIfEmpty() is not null && dbAttribute.Namespace != plan.Namespace) { if (plan.IsDefaultNamespace) { plan.IsDefaultNamespace = false; plan.Namespace = dbAttribute.Namespace !; plan.ClassModel = classModel; } else { outputModel.Report(Diagnostics.Errors.DbMultipleNamespace, symbol); } } } else { plan = new DatabasePlan { Namespace = dbAttribute.Namespace.NullIfEmpty() !, Name = name, ClassModel = classModel, DbClassName = name.WithSuffix(Suffixes.Database), PartitionsClassName = name.WithSuffix(Suffixes.Partitions), QueryBuilderClassName = name.WithSuffix(Suffixes.QueryBuilder), QueryBuilderUnionsClassName = name.WithSuffix(Suffixes.QueryBuilderUnions), QueryClassName = name.WithSuffix(Suffixes.Query), QueryUnionsClassName = name.WithSuffix(Suffixes.QueryUnions), ReadClassName = name.WithSuffix(Suffixes.Read), SerializerClassName = name.WithSuffix(Suffixes.Serializer), ConverterClassName = name.WithSuffix(Suffixes.Converter), TypesClassName = name.WithSuffix(Suffixes.Types), BatchHandlersClassName = name.WithSuffix(Suffixes.BatchHandlers), ChangeFeedProcessorClassName = name.WithSuffix(Suffixes.ChangeFeedProcessor) }; plan.BatchHandlersArgumentName = plan.BatchHandlersClassName.ToArgumentName(); plan.IsDefaultNamespace = plan.Namespace is null; plan.Namespace ??= classModel?.ClassSymbol.ContainingNamespace?.ToDisplayString() ?? outputModel.Compilation.Assembly.Name.NullIfEmpty() ?? "Cosmogenesis.Generated"; plan.DbClassNameArgument = plan.DbClassName.ToArgumentName(); plan.QueryBuilderClassNameArgument = plan.QueryBuilderClassName.ToArgumentName(); outputModel.ValidateNames( symbol, plan.Name, plan.DbClassName, plan.PartitionsClassName, plan.QueryBuilderClassName, plan.QueryClassName, plan.ReadClassName, plan.SerializerClassName, plan.ConverterClassName, plan.TypesClassName, plan.BatchHandlersClassName, plan.ChangeFeedProcessorClassName, plan.QueryBuilderUnionsClassName, plan.QueryUnionsClassName); outputModel.ValidateIdentifiers( symbol, plan.BatchHandlersArgumentName, plan.DbClassNameArgument, plan.QueryBuilderClassNameArgument); plan.Namespace.ValidateNamespace(outputModel, symbol); outputPlan.DatabasePlansByName[name] = plan; } if (classModel is not null) { outputPlan.DatabasePlansByClass[classModel].Add(plan); } } }
public static void AddDocuments(OutputModel outputModel, OutputPlan outputPlan) { outputModel.CancellationToken.ThrowIfCancellationRequested(); if (!outputModel.CanGenerate) { return; } foreach (var kvp in outputPlan.DatabasePlansByClass) { var classModel = kvp.Key; var databasePlans = kvp.Value; if (classModel.IsDbDoc && !classModel.ClassSymbol.IsAbstract) { if (databasePlans.Count == 0) { outputModel.Report(Diagnostics.Errors.NoDatabase, classModel.ClassSymbol); } else if (classModel.PartitionAttribute is null) { outputModel.Report(Diagnostics.Warnings.DbDocWithoutPartition, classModel.ClassSymbol, classModel.ClassSymbol.Name); } else { if (!classModel.ClassSymbol.Constructors.Any(x => x.Parameters.IsDefaultOrEmpty && x.DeclaredAccessibility.IsAccessible())) { outputModel.Report(Diagnostics.Errors.ParameterlessConstructor, classModel.ClassSymbol); } var implicitGetIds = classModel.Methods .Where(x => x.MethodSymbol.Name == "GetId") .Where(x => x.MethodSymbol.IsStatic) .Where(x => x.MethodSymbol.ReturnType.SpecialType == SpecialType.System_String) .Where(x => x.MethodSymbol.DeclaredAccessibility.IsAccessible()) .ToList(); if (implicitGetIds.Count == 0) { outputModel.Report(Diagnostics.Errors.NoGetId, classModel.ClassSymbol); } else if (implicitGetIds.Count > 1) { outputModel.Report(Diagnostics.Errors.MultipleGetId, classModel.ClassSymbol); } else { var getId = implicitGetIds[0]; var partitionName = classModel.PartitionAttribute.Name; var name = classModel.ClassSymbol.Name.WithoutSuffix(Suffixes.Doc).WithoutSuffix(Suffixes.Document); var type = classModel.DocTypeAttribute?.Name.NullIfEmpty() ?? name; type.ValidateDocType(outputModel, classModel.ClassSymbol); foreach (var databasePlan in databasePlans) { if (!databasePlan.PartitionPlansByName.TryGetValue(partitionName, out var partitionPlan)) { outputModel.Report(Diagnostics.Errors.NoGetPk, classModel.ClassSymbol); } else { var documentPlan = new DocumentPlan { DocType = type, FullTypeName = classModel.ClassSymbol.ToDisplayString(), ClassModel = classModel, IsMutable = classModel.MutableAttribute is not null, IsTransient = classModel.TransientAttribute is not null, ClassName = name, ClassNameArgument = name.ToArgumentName(), PluralName = name.Pluralize(), ConstDocType = $"{databasePlan.Namespace}.{databasePlan.TypesClassName}.{partitionPlan.ClassName}.{name}", GetIdPlan = new GetPkIdPlan { FullMethodName = $"{getId.MethodSymbol.ContainingType.ToDisplayString()}.{getId.MethodSymbol.Name}", Arguments = getId .MethodSymbol .Parameters .Select(x => new GetPkIdPlan.Argument { ArgumentName = x.Name.ToArgumentName(), FullTypeName = x.Type.ToDisplayString() }) .ToList() } }; partitionPlan.Documents.Add(documentPlan); outputModel.ValidateNames( classModel.ClassSymbol, documentPlan.ClassName, documentPlan.PluralName); outputModel.ValidateIdentifiers( classModel.ClassSymbol, documentPlan.ClassNameArgument); PropertyPlanBuilder.AddProperties(outputModel, classModel, documentPlan); documentPlan.GetIdPlan = GetPkIdPlanBuilder.Build(outputModel, getId.MethodSymbol, documentPlan.PropertiesByName, documentPlan.PropertiesByArgumentName); if (partitionPlan.GetPkPlan is null) { partitionPlan.GetPkPlan = GetPkIdPlanBuilder.Build(outputModel, partitionPlan.GetPkModel.MethodSymbol, documentPlan.PropertiesByName, documentPlan.PropertiesByArgumentName); } else { foreach (var arg in partitionPlan.GetPkPlan.Arguments) { if (arg.PropertyName is not null && !documentPlan.PropertiesByName.ContainsKey(arg.PropertyName)) { outputModel.Report(Diagnostics.Errors.PropertyResolvePkIdConsistency, partitionPlan.GetPkModel.MethodSymbol, arg.ArgumentName, arg.PropertyName); } } } } } } } } } } }