Skip to content

Commit

Permalink
Merge pull request #7 from CodeFactoryLLC/StandardNDF
Browse files Browse the repository at this point in the history
nuget release
  • Loading branch information
sgiffinlcd authored Jun 7, 2023
2 parents 26b5ab5 + 13bc2d8 commit cf7aa75
Show file tree
Hide file tree
Showing 20 changed files with 1,123 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeFactory.WinVs;
using CodeFactory.WinVs.Models.CSharp;
using CodeFactory.WinVs.Models.CSharp.Builder;

namespace CodeFactory.Automation.Standard.NDF.Logic
{
/// <summary>
/// Logic for adding missing members.
/// </summary>
public static class AddMissingMembers
{
/// <summary>
/// Name of the Microsoft logging library.
/// </summary>
public const string MicrosoftLoggingNamespace = "Microsoft.Extensions.Logging";

/// <summary>
/// Name of the NDF namespace.
/// </summary>
public const string NDFNamespace = "CodeFactory.NDF";

/// <summary>
/// Add missing interface members
/// </summary>
/// <param name="source">The CodeFactory automation for Visual Studio Windows</param>
/// <param name="sourceCode">Source code model to be updated with add members in the target class.</param>
/// <param name="updateClass">Class model to add missing members to.</param>
/// <param name="supportsLogging">Flag that determines if logging is enabled.</param>
/// <param name="loggerFieldName">Optional, the name of the field to use for logging.</param>
/// <returns></returns>
public static async Task<CsClass> AddMissingMembersStandardNDFAsync(this IVsActions source, CsSource sourceCode,
CsClass updateClass, bool supportsLogging, string loggerFieldName = "_logger")
{
//Bounds checks to make sure all data needed is provided.
if (sourceCode == null)
throw new CodeFactoryException(
"Visual Studio automation for CodeFactory was not provided cannot add missing members.");

if (sourceCode == null)
throw new CodeFactoryException("No source code was provided, cannot add the missing members.");

if (updateClass == null)
throw new CodeFactoryException(
"No target class to add missing members was provided, cannot add the missing members.");

//Get the missing members to be added
var missingMembers = updateClass.GetMissingInterfaceMembers();

//If no missing members are found just return the current class.
if (!missingMembers.Any()) return updateClass;

//Creating the source code manager for the class.
var manager = new SourceClassManager(sourceCode, updateClass, source);
manager.LoadNamespaceManager();


//Creating the blocks to be used for code generation
ILoggerBlock loggerBlock = supportsLogging ? new LoggerBlockNDF(loggerFieldName) : null;

var boundsChecks = new IBoundsCheckBlock[]
{
new BoundsCheckBlockStringNDF(true, loggerBlock),
new BoundsCheckBlockNullNDF(true, loggerBlock)
};

var catchBlocks = new ICatchBlock[]
{
new CatchBlockManagedExceptionNDF(loggerBlock),
new CatchBlockExceptionNDF(loggerBlock)
};

ITryBlock tryBlock = new TryBlockStandard(loggerBlock, catchBlocks);

if(supportsLogging) await manager.UsingStatementAddAsync(MicrosoftLoggingNamespace);
await manager.UsingStatementAddAsync(NDFNamespace);

//Creating the builders to generate code by member type.
IMethodBuilder methodBuilder = new MethodBuilderStandard(loggerBlock, boundsChecks, tryBlock);
IPropertyBuilder propertyBuilder = new PropertyBuilderStandard();
IEventBuilder eventBuilder = new EventBuilderStandard();

//Process all missing properties.
var missingProperties = missingMembers.Where(m => m.MemberType == CsMemberType.Property).Cast<CsProperty>()
.ToList();

foreach (var missingProperty in missingProperties)
{
var propertySyntax = await propertyBuilder.BuildPropertyAsync(missingProperty, manager, 2);

if(propertySyntax == null) continue;

await manager.PropertiesAddAfterAsync(propertySyntax);

}

//Process all missing methods.
var missingMethods = missingMembers.Where(m => m.MemberType == CsMemberType.Method).Cast<CsMethod>()
.ToList();

foreach (var missingMethod in missingMethods)
{
var methodSyntax = await methodBuilder.BuildMethodAsync(missingMethod, manager, 2);

if(methodSyntax == null) continue;

await manager.MethodsAddAfterAsync(methodSyntax);
}

//Process all missing events.
var missingEvents = missingMembers.Where(m => m.MemberType == CsMemberType.Event).Cast<CsEvent>()
.ToList();

foreach (var missingEvent in missingEvents)
{
var eventSyntax = await eventBuilder.BuildEventAsync(missingEvent, manager, 2);

if(eventSyntax == null) continue;

await manager.EventsAddAfterAsync(eventSyntax);
}

return manager.Container;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeFactory.Automation.Standard.NDF.Logic
{
/// <summary>
/// Data class that holds constants used in AspNet Delivery.
/// </summary>
public static class AspNetConstants
{
/// <summary>
/// The name of the filed that holds the logger
/// </summary>
public const string FieldNameLogger = "_logger";

/// <summary>
/// The full name of the library for logging.
/// </summary>
public const string MicrosoftExtensionLibraryForLoggingName = "Microsoft.Extensions.Logging.Abstractions";

/// <summary>
/// Library name for the Microsoft Logger abstractions library.
/// </summary>
public const string MicrosoftLoggerLibraryName = "Microsoft.Extensions.Logging.Abstractions";

/// <summary>
/// The default namespace for the Microsoft extensions logger implementation
/// </summary>
public const string MicrosoftLoggerNamespace = "Microsoft.Extensions.Logging";

/// <summary>
/// The name of the interface for the Microsoft extensions logger.
/// </summary>
public const string MicrosoftLoggerInterfaceName = "ILogger";

/// <summary>
/// The fully qualified name of the a aspnet core controller.
/// </summary>
public const string ControllerBaseName = "Microsoft.AspNetCore.Mvc.ControllerBase";

/// <summary>
/// The full namespace for the mvc namespace.
/// </summary>
public const string MvcNamespace = "Microsoft.AspNetCore.Mvc";

/// <summary>
/// Name for the abstract classes that support action result.
/// </summary>
public const string ActionResultClassName = "ActionResult";

/// <summary>
/// Name of the interface for action results.
/// </summary>
public const string ActionResultInterfaceName = "IActionResult";

/// <summary>
/// Namespace for tasks
/// </summary>
public const string SystemThreadingTasksNamespace = "System.Threading.Tasks";

/// <summary>
/// Class name for the Task class.
/// </summary>
public const string TaskClassName = "Task";

/// <summary>
/// Namespace for dependency injection.
/// </summary>
public const string DependencyInjectionAbstractions = "Microsoft.Extensions.DependencyInjection.Abstractions";

/// <summary>
/// Namespace for configuration.
/// </summary>
public const string ConfigurationAbstractions = "Microsoft.Extensions.Configuration.Abstractions";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeFactory.WinVs.Models.CSharp;
using CodeFactory.WinVs.Models.CSharp.Builder;
using Microsoft.Extensions.Logging;

namespace CodeFactory.Automation.Standard.NDF.Logic
{
/// <summary>
/// Null bounds check that support NDF logging and exceptions.
/// </summary>
internal class BoundsCheckBlockNullNDF:BaseBoundsCheckBlock
{
/// <summary>Initializes the base class for the bounds check.</summary>
/// <param name="ignoreWhenDefaultValueIsSet">Flag that determines if the bounds checking should be ignored if a default value is set.</param>
/// <param name="loggerBlock">Logger block used with bounds check logic.</param>
public BoundsCheckBlockNullNDF(bool ignoreWhenDefaultValueIsSet, ILoggerBlock loggerBlock) : base(nameof(BoundsCheckBlockNullNDF), ignoreWhenDefaultValueIsSet, loggerBlock)
{
//Intentionally blank
}

/// <summary>
/// Generates the bounds check syntax if the parameter meets the criteria for a bounds check.
/// </summary>
/// <param name="sourceMethod">The target method the parameter belongs to.</param>
/// <param name="checkParameter">The parameter to build the bounds check for.</param>
/// <returns>Returns a tuple that contains a boolean that determines if the bounds check syntax was created for the parameter.</returns>
public override (bool hasBoundsCheck, string boundsCheckSyntax) GenerateBoundsCheck(CsMethod sourceMethod, CsParameter checkParameter)
{
//bounds check to make sure we have parameter data.
if (checkParameter == null) return (false, null);

if(checkParameter.ParameterType.IsValueType) return (false, null);

if(checkParameter.HasDefaultValue) return (false, null);

SourceFormatter formatter = new SourceFormatter();

formatter.AppendCodeLine(0,$"if ({checkParameter.Name} == null)");
formatter.AppendCodeLine(0,"{");
if (LoggerBlock != null)
{
var errorMessage = $"$\"The parameter {{nameof({checkParameter.Name})}} was not provided. Will raise an argument exception\"";
formatter.AppendCodeLine(1,LoggerBlock.GenerateLogging(LogLevel.Error, errorMessage,true));
formatter.AppendCodeLine(1, LoggerBlock.GenerateExitLogging(LogLevel.Error,sourceMethod.Name));
}
formatter.AppendCodeLine(1,$"throw new ArgumentNullException(nameof({checkParameter.Name}));");
formatter.AppendCodeLine(0,"}");

return (true,formatter.ReturnSource());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeFactory.WinVs.Models.CSharp;
using CodeFactory.WinVs.Models.CSharp.Builder;
using Microsoft.Extensions.Logging;

namespace CodeFactory.Automation.Standard.NDF.Logic
{
/// <summary>
/// String bounds check that supports NDF logging and exception management.
/// </summary>
public class BoundsCheckBlockStringNDF:BaseBoundsCheckBlock
{
/// <summary>Initializes the base class for the bounds check.</summary>
/// <param name="ignoreWhenDefaultValueIsSet">Flag that determines if the bounds checking should be ignored if a default value is set.</param>
/// <param name="loggerBlock">Logger block used with bounds check logic.</param>
public BoundsCheckBlockStringNDF(bool ignoreWhenDefaultValueIsSet, ILoggerBlock loggerBlock) : base(nameof(BoundsCheckBlockStringNDF), ignoreWhenDefaultValueIsSet, loggerBlock)
{
}

/// <summary>
/// Generates the bounds check syntax if the parameter meets the criteria for a bounds check.
/// </summary>
/// <param name="sourceMethod">The target method the parameter belongs to.</param>
/// <param name="checkParameter">The parameter to build the bounds check for.</param>
/// <returns>Returns a tuple that contains a boolean that determines if the bounds check syntax was created for the parameter.</returns>
public override (bool hasBoundsCheck, string boundsCheckSyntax) GenerateBoundsCheck(CsMethod sourceMethod, CsParameter checkParameter)
{
//bounds check to make sure we have parameter data.
if (checkParameter == null) return (false, null);

if(!(checkParameter.ParameterType.Namespace == "System" & checkParameter.ParameterType.Name == "String")) return (false, null);

if(checkParameter.HasDefaultValue) return (false, null);

SourceFormatter formatter = new SourceFormatter();

formatter.AppendCodeLine(0,$"if (string.IsNullOrEmpty({checkParameter.Name}))");
formatter.AppendCodeLine(0,"{");
if (LoggerBlock != null)
{
var errorMessage =
$"$\"The parameter {{nameof({checkParameter.Name})}} was not provided. Will raise an argument exception\"";
formatter.AppendCodeLine(1,LoggerBlock.GenerateLogging(LogLevel.Error, errorMessage,true));
formatter.AppendCodeLine(1, LoggerBlock.GenerateExitLogging(LogLevel.Error,sourceMethod.Name));
}
formatter.AppendCodeLine(1,$"throw new ArgumentException(nameof({checkParameter.Name}));");
formatter.AppendCodeLine(0,"}");

return (true,formatter.ReturnSource());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeFactory.WinVs.Models.CSharp.Builder;
using Microsoft.Extensions.Logging;

namespace CodeFactory.Automation.Standard.NDF.Logic
{
/// <summary>
/// CodeBlock that builds a catch block for DBUpdateException and returns a NDF managed exception.
/// </summary>
public class CatchBlockDBUpdateExceptionNDF:BaseCatchBlock
{

/// <summary>
/// Creates a instances of the <see cref="CatchBlockDBUpdateExceptionNDF"/>
/// </summary>
/// <param name="loggerBlock">Optional, logger block to use for logging in the catch block.</param>
public CatchBlockDBUpdateExceptionNDF(ILoggerBlock loggerBlock = null) : base(loggerBlock)
{
//intentionally bank
}

/// <summary>Builds the catch block</summary>
/// <param name="syntax">Syntax to be injected into the catch block, optional parameter.</param>
/// <param name="multipleSyntax">Multiple syntax statements has been provided to be used by the catch block,optional parameter.</param>
/// <param name="memberName">Optional parameter that determines the target member the catch block is implemented in.</param>
/// <returns>Returns the generated catch block</returns>
protected override string BuildCatchBlock(string syntax = null, IEnumerable<NamedSyntax> multipleSyntax = null, string memberName = null)
{
SourceFormatter formatter = new SourceFormatter();

formatter.AppendCodeLine(0,"catch (DbUpdateException updateDataException)");
formatter.AppendCodeLine(0,"{");
formatter.AppendCodeLine(1, "var sqlError = updateDataException.InnerException as SqlException;");
formatter.AppendCodeLine(1);
formatter.AppendCodeLine(1,"if (sqlError == null)");
formatter.AppendCodeLine(1,"{");
if (LoggerBlock != null)
{
formatter.AppendCodeLine(2, LoggerBlock.GenerateLogging(LogLevel.Error, "The following database error occurred.",false,"updateDataException"));
formatter.AppendCodeLine(2, LoggerBlock.GenerateExitLogging(LogLevel.Error, memberName));
}
formatter.AppendCodeLine(2, "throw new DataException();");
formatter.AppendCodeLine(1,"}");


if (LoggerBlock != null)
{
formatter.AppendCodeLine(1, LoggerBlock.GenerateLogging(LogLevel.Error, "The following SQL exception occurred.",false, "sqlError"));
formatter.AppendCodeLine(1, LoggerBlock.GenerateExitLogging(LogLevel.Error, memberName));
}
formatter.AppendCodeLine(1,"sqlError.ThrowManagedException();");
formatter.AppendCodeLine(0,"}");

return formatter.ReturnSource();
}
}
}
Loading

0 comments on commit cf7aa75

Please sign in to comment.