Example #1
0
        /// <summary>
        /// Builds the services registration method. This will contain the transient registrations for each class in the target project.
        /// This will return a signature of [Public/Private] [static] void [methodName](IServiceCollection [collectionParameterName])
        /// With a body that contains the full transient registrations.
        /// </summary>
        /// <param name="registrations">The registrations to be added.</param>
        /// <param name="isPublicMethod">Flag that determines if the method is public or private in scope.</param>
        /// <param name="isStatic">Flag to determine if the method should be defined as a static or instance method.</param>
        /// <param name="methodName">The target name of the method to be created.</param>
        /// <param name="serviceCollectionParameterName">The name of the service collection parameter where transient registrations will take place.</param>
        /// <param name="manager">The namespace manager that will be used to shorten type name registration with dependency injection. This will need to be loaded from the target class.</param>
        /// <returns>The formatted method.</returns>
        public static string BuildTransientInjectionMethod(IEnumerable <DependencyInjectionRegistrationInformation> registrations, bool isPublicMethod, bool isStatic, string methodName, string serviceCollectionParameterName, NamespaceManager manager = null)
        {
            CodeFactory.SourceFormatter registrationFormatter = new CodeFactory.SourceFormatter();

            string methodSecurity = isPublicMethod ? Security.Public : Security.Private;

            string methodSignature = isStatic
                ? $"{methodSecurity} static void {methodName}(IServiceCollection {serviceCollectionParameterName})"
                : $"{methodSecurity} void {methodName}(IServiceCollection {serviceCollectionParameterName})";

            registrationFormatter.AppendCodeLine(0, "/// <summary>");
            registrationFormatter.AppendCodeLine(0, "/// Automated registration of classes using transient registration.");
            registrationFormatter.AppendCodeLine(0, "/// </summary>");
            registrationFormatter.AppendCodeLine(0, $"/// <param name=\"{serviceCollectionParameterName}\">The service collection to register services.</param>");
            registrationFormatter.AppendCodeLine(0, methodSignature);
            registrationFormatter.AppendCodeLine(0, "{");
            registrationFormatter.AppendCodeLine(1, "//This method was auto generated, do not modify by hand!");
            foreach (var registration in registrations)
            {
                var registrationEntry = FormatTransientRegistrationForDependencyInjection(registration.ClassData, serviceCollectionParameterName, registration.InterfaceData, manager);
                if (registrationEntry != null)
                {
                    registrationFormatter.AppendCodeLine(1, registrationEntry);
                }
            }
            registrationFormatter.AppendCodeLine(0, "}");

            return(registrationFormatter.ReturnSource());
        }
        /// <summary>
        /// Implements a default event implementation for a missing member.
        /// </summary>
        /// <param name="memberData">Event data to be loaded.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <returns>The fully formatted event source code or null if the member could not be implemented.</returns>
        public static string FormatMemberEvent(CsEvent memberData, NamespaceManager manager)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //Using the formatter helper to generate a default event signature.
            string eventSyntax = memberData.CSharpFormatEventDeclaration(manager);

            //If the event syntax was not created return.
            if (string.IsNullOrEmpty(eventSyntax))
            {
                return(null);
            }

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }

            //Adding the event declaration
            formatter.AppendCodeLine(0, eventSyntax);

            //Adding a extra line feed at the end of the declaration.
            formatter.AppendCodeLine(0);

            //The source formatter returning the final results.
            return(formatter.ReturnSource());
        }
        /// <summary>
        /// Extension method that determines if the logger field is implemented in the class. If it exists will return the provided source. Otherwise will add the logging namespace and the logger field.
        /// </summary>
        /// <param name="source">Source class to check for the logger field.</param>
        /// <param name="loggerName">The name of the logger field to check for.</param>
        /// <param name="parentSourceCode">The source code the class was loaded from.</param>
        /// <returns>The existing source code if the field is found, or the updated source code with the logging field added.</returns>
        public static async Task <CsSource> AddMicrosoftExtensionsLoggerFieldAsync(this CsClass source, string loggerName,
                                                                                   CsSource parentSourceCode)
        {
            //Bounds checking
            if (source == null)
            {
                return(parentSourceCode);
            }
            if (String.IsNullOrEmpty(loggerName))
            {
                return(parentSourceCode);
            }

            //Checking to see if the logger field already exists. If it does just return the parent source code.
            if (source.HasMicrosoftExtensionsLoggerField(loggerName))
            {
                return(parentSourceCode);
            }

            //Adding the logging namespace
            var currentSource = await parentSourceCode.AddUsingStatementAsync(NetConstants.MicrosoftLoggerNamespace);

            var currentClass = currentSource.GetModel(source.LookupPath) as CsClass;

            if (currentClass == null)
            {
                throw new CodeFactoryException("Cannot load class data to add the logger field.");
            }

            CodeFactory.SourceFormatter fieldSource = new CodeFactory.SourceFormatter();

            fieldSource.AppendCodeLine(2, "/// <summary>");
            fieldSource.AppendCodeLine(2, "/// Logger for all logging interactions in the class.");
            fieldSource.AppendCodeLine(2, "/// </summary>");
            fieldSource.AppendCodeLine(2, $"private readonly {NetConstants.MicrosoftLoggerInterfaceName} {loggerName};");
            fieldSource.AppendCodeLine(0);

            currentSource = await currentClass.AddToBeginningAsync(fieldSource.ReturnSource());

            return(currentSource);
        }
        /// <summary>
        /// Implements a common delivery framework method implementation for a missing member.
        /// </summary>
        /// <param name="memberData">Event data to be loaded.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <param name="includeLogging">Flag that determines if enter, exit, and error logging should be included in a method implementation.</param>
        /// <param name="includeBoundsCheck">Flag that determines if string and nullable type bounds checking should be included in a method implementation.</param>
        /// <param name="supportAsyncKeyword">Flag that determines if methods should be implemented with the async keyword when supported by the method implementation.</param>
        /// <param name="isControllerAction">Flag that determines if the the method is a controller action. This will alter the error management logic.</param>
        /// <returns>The fully formatted method source code or null if the member could not be implemented.</returns>
        public static string FormatMemberMethod(CsMethod memberData, NamespaceManager manager, bool includeLogging,
                                                bool includeBoundsCheck, bool supportAsyncKeyword, bool isControllerAction)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //Using the formatter helper to generate a method signature.
            string methodSyntax = supportAsyncKeyword
                ? memberData.CSharpFormatStandardMethodSignatureWithAsync(manager)
                : memberData.CSharpFormatStandardMethodSignature(manager);

            //If the method syntax was not created return.
            if (string.IsNullOrEmpty(methodSyntax))
            {
                return(null);
            }

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }

            //Adding the method declaration
            formatter.AppendCodeLine(0, methodSyntax);
            formatter.AppendCodeLine(0, "{");

            //Adding enter logging if logging is enabled.
            if (includeLogging)
            {
                formatter.AppendCodeLine(1, "_logger.InformationEnterLog();");
                formatter.AppendCodeLine(0);
            }

            //Processing each parameter for bounds checking if bounds checking is enabled.
            if (includeBoundsCheck & memberData.HasParameters)
            {
                foreach (ICsParameter paramData in memberData.Parameters)
                {
                    //If the parameter has a default value then continue will not bounds check parameters with a default value.
                    if (paramData.HasDefaultValue)
                    {
                        continue;
                    }

                    //If the parameter is a string type add the following bounds check
                    if (paramData.ParameterType.WellKnownType == CsKnownLanguageType.String)
                    {
                        //Adding an if check
                        formatter.AppendCodeLine(1, $"if(string.IsNullOrEmpty({paramData.Name}))");
                        formatter.AppendCodeLine(1, "{");

                        //If logging was included add error logging and exit logging
                        if (includeLogging)
                        {
                            formatter.AppendCodeLine(2,
                                                     $"_logger.ErrorLog($\"The parameter {{nameof({paramData.Name})}} was not provided. Will raise an argument exception\");");
                            formatter.AppendCodeLine(2, "_logger.InformationExitLog();");
                        }

                        //Adding a throw of an argument null exception
                        formatter.AppendCodeLine(2, $"throw new ValidationException(nameof({paramData.Name}));");
                        formatter.AppendCodeLine(1, "}");
                        formatter.AppendCodeLine(0);
                    }

                    // Check to is if the parameter is not a value type or a well know type if not then go ahead and perform a null bounds check.
                    if (!paramData.ParameterType.IsValueType & !paramData.ParameterType.IsWellKnownType)
                    {
                        //Adding an if check
                        formatter.AppendCodeLine(1, $"if({paramData.Name} == null)");
                        formatter.AppendCodeLine(1, "{");

                        //If logging was included add error logging and exit logging
                        if (includeLogging)
                        {
                            formatter.AppendCodeLine(2,
                                                     $"_logger.ErrorLog($\"The parameter {{nameof({paramData.Name})}} was not provided. Will raise an argument exception\");");
                            formatter.AppendCodeLine(2, "_logger.InformationExitLog();");
                        }

                        //Adding a throw of an argument null exception
                        formatter.AppendCodeLine(2, $"throw new ValidationException(nameof({paramData.Name}));");
                        formatter.AppendCodeLine(1, "}");
                        formatter.AppendCodeLine(0);
                    }
                }
            }

            //Formatting standard try block for method
            formatter.AppendCodeLine(1, "try");
            formatter.AppendCodeLine(1, "{");
            formatter.AppendCodeLine(2, "//TODO: add execution logic here");
            formatter.AppendCodeLine(1, "}");

            //Formatting managed exception block for method
            if (!isControllerAction)
            {
                formatter.AppendCodeLine(1, "catch (ManagedException)");
                formatter.AppendCodeLine(1, "{");
                formatter.AppendCodeLine(2, "//Throwing the managed exception. Override this logic if you have logic in this method to handle managed errors.");
                formatter.AppendCodeLine(2, "throw;");
                formatter.AppendCodeLine(1, "}");
            }
            else
            {
                formatter.AppendCodeLine(1, "catch (ManagedException managedException)");
                formatter.AppendCodeLine(1, "{");
                formatter.AppendCodeLine(2, "//Throwing the managed exception. Override this logic if you have logic in this method to handle managed errors.");
                formatter.AppendCodeLine(2, "_logger.InformationExitLog();");
                formatter.AppendCodeLine(2, "return this.CreateProblemResult(managedException);");
                formatter.AppendCodeLine(1, "}");
            }

            //Formatting standard catch block for method
            formatter.AppendCodeLine(1, "catch (Exception unhandledException)");
            formatter.AppendCodeLine(1, "{");

            if (!isControllerAction)
            {
                formatter.AppendCodeLine(2,
                                         "_logger.ErrorLog(\"An unhandled exception occured, see the exception for details. Will throw a UnhandledException\", unhandledException);");
                formatter.AppendCodeLine(2, "_logger.InformationExitLog();");
                formatter.AppendCodeLine(2, "throw new UnhandledException();");
            }
            else
            {
                formatter.AppendCodeLine(2, " _logger.ErrorLog(\"An unhandled exception occured, see the exception for details. Will throw a UnhandledLogicException\", unhandledException);");
                formatter.AppendCodeLine(2, "_logger.InformationExitLog();");
                formatter.AppendCodeLine(2, "var unhandledError = new UnhandledException();");
                formatter.AppendCodeLine(2, "return this.CreateProblemResult(unhandledError);");
            }

            formatter.AppendCodeLine(1, "}");
            formatter.AppendCodeLine(0);

            //If logging add a logging exit statement.
            formatter.AppendCodeLine(1, "_logger.InformationExitLog();");

            //Add an exception for not implemented until the developer updates the logic.
            formatter.AppendCodeLine(1, "throw new NotImplementedException();");

            //if the return type is not void then add a to do message for the developer to add return logic.
            if (!memberData.IsVoid)
            {
                formatter.AppendCodeLine(0);
                formatter.AppendCodeLine(1, "//TODO: add return logic here");
            }
            formatter.AppendCodeLine(0, "}");
            formatter.AppendCodeLine(0);

            //Returning the fully formatted method.
            return(formatter.ReturnSource());
        }
        /// <summary>
        /// Adds the missing members to a target class, that support the common delivery framework implementation..
        /// </summary>
        /// <param name="source">The source code that contains the target class to be updated.</param>
        /// <param name="targetClass">The target class to have the members added to.</param>
        /// <param name="missingMembers">The missing members to be added to the target class.</param>
        /// <param name="boundsCheckLogic">Optional parameter that determines if bounds checking logic should be added to methods. This will default to true.</param>
        /// <param name="loggingLogic">Optional parameter that determines if enter, exit and exception management logic should be added to methods.</param>
        /// <param name="supportAsyncMethods">Optional parameter that determines if methods will use the async keyword when support multi-threaded operations.</param>
        /// <param name="supportsCDFAspNet">Optional parameters that determines if add members support aspnet error handling with CommonDeliveryFramework.</param>
        /// <returns>The updated SourceCode Model once the missing members have been added.</returns>
        /// <exception cref="ArgumentNullException">Thrown if data needed to process a member is not passed.</exception>
        /// <exception cref="CodeFactoryException">Thrown is model data is not valid or a processing error occurs.</exception>
        public static async Task <CsSource> AddMembersToClassWithCDFSupportAsync(CsSource source, CsClass targetClass,
                                                                                 IReadOnlyList <CsMember> missingMembers, bool boundsCheckLogic = true, bool loggingLogic = true, bool supportAsyncMethods = true, bool supportsCDFAspNet = false)
        {
            //Bounds checking to confirm all needed data was passed.
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (targetClass == null)
            {
                throw new ArgumentNullException(nameof(targetClass));
            }
            if (missingMembers == null)
            {
                throw new ArgumentNullException(nameof(missingMembers));
            }

            //Confirming the target models are loaded and are source code
            if (!source.IsLoaded)
            {
                throw new CodeFactoryException("Source code model was not loaded.");
            }
            if (!targetClass.IsLoaded)
            {
                throw new CodeFactoryException("The target class model data is not loaded.");
            }
            if (!targetClass.LoadedFromSource)
            {
                throw new CodeFactoryException("Target class is not loaded from source code cannot update.");
            }

            //If an empty list of missing members was provided then return the original source code
            if (!missingMembers.Any())
            {
                return(source);
            }

            //Variables used to hold the most current version of the code factory models that are being updated.
            CsClass  currentClassModel  = targetClass;
            CsSource currentSourceModel = source;

            currentSourceModel = await currentSourceModel.AddMissingNamespaces(missingMembers, currentClassModel.Namespace);

            currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
            if (currentClassModel == null)
            {
                throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
            }

            //Checking to make sure there is a logger field implemented if not add it to the class.
            currentSourceModel = await currentClassModel.AddMicrosoftExtensionsLoggerFieldAsync(AspNetCoreConstants.FieldNameLogger,
                                                                                                currentSourceModel);

            currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
            if (currentClassModel == null)
            {
                throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
            }


            currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.CommonDeliveryFrameworkLibraryName);

            currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
            if (currentClassModel == null)
            {
                throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
            }

            if (supportsCDFAspNet)
            {
                //Confirming CDF ASPnet namespace is added to the code file.
                currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.CommonDeliveryFrameworkAspNetLibraryName);

                currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
                if (currentClassModel == null)
                {
                    throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
                }

                //Confirming the mvc namespace is added to the code file.
                currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.MvcNamespace);

                currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
                if (currentClassModel == null)
                {
                    throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
                }
            }

            if (supportAsyncMethods)
            {
                //Confirming the mvc namespace is added to the code file.
                currentSourceModel = await currentSourceModel.AddUsingStatementAsync(AspNetCoreConstants.SystemThreadingTasksNamespace);

                currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
                if (currentClassModel == null)
                {
                    throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
                }
            }

            //Loading a namespace manager that will update types definitions to use the correct namespace format. This reads in all the using statements assigned to the class.
            var namespaceManager = source.LoadNamespaceManager(currentClassModel.Namespace);

            bool isControllerClass = currentClassModel.IsController();

            //Processing the missing members
            foreach (var member in missingMembers)
            {
                //Clearing the formatted source code for the member before processing each member
                string sourceCode = null;

                switch (member.MemberType)
                {
                case CsMemberType.Event:

                    //Validating the member event model loaded correctly if not return to the top. No exception will be thrown.
                    if (!(member is CsEvent eventModel))
                    {
                        continue;
                    }

                    //Formatting a default event definition.
                    sourceCode = FormatMemberEvent(eventModel, namespaceManager);

                    break;

                case CsMemberType.Method:

                    //Validating the member event model loaded correctly if not return to the top. No exception will be thrown.
                    if (!(member is CsMethod methodModel))
                    {
                        continue;
                    }

                    bool isControllerAction = false;

                    if (supportsCDFAspNet & isControllerClass)
                    {
                        isControllerAction = IsControllerAction(methodModel);
                    }

                    //Formatting the method implementation based on the optional parameters provided.
                    sourceCode = FormatMemberMethod(methodModel, namespaceManager, loggingLogic, boundsCheckLogic,
                                                    supportAsyncMethods, isControllerAction);

                    break;

                case CsMemberType.Property:

                    //Validating the member property model loaded correctly if not return to the top. No exception will be thrown.
                    if (!(member is CsProperty propertyModel))
                    {
                        continue;
                    }

                    //Formatting a default property definition.
                    sourceCode = FormatMemberProperty(propertyModel, namespaceManager);

                    break;

                default:
                    continue;
                }

                if (string.IsNullOrEmpty(sourceCode))
                {
                    continue;
                }

                //Creating a source formatter and appending the final output with two indent levels to within the body over a class.
                var sourceFormatter = new CodeFactory.SourceFormatter();

                sourceFormatter.AppendCodeBlock(2, sourceCode);

                currentSourceModel = await currentClassModel.AddToEndAsync(sourceFormatter.ReturnSource());

                if (currentSourceModel == null)
                {
                    throw new CodeFactoryException("Cannot load the source code file, cannot complete adding interface members");
                }

                currentClassModel = currentSourceModel.GetModel(currentClassModel.LookupPath) as CsClass;
                if (currentClassModel == null)
                {
                    throw new CodeFactoryException("Cannot load class data, cannot complete adding interface members");
                }
            }

            return(currentSourceModel);
        }
Example #6
0
        /// <summary>
        /// Create a new registration class from scratch
        /// </summary>
        /// <param name="sourceProject">The source project to build the new registration class for.</param>
        /// <param name="className">The name of the class that is used for service registration. This will be set to the constant <see cref="NetConstants.DefaultDependencyInjectionClassName"/> this can be overwritten with a custom class name.</param>
        /// <param name="automatedRegistrationMethodName">The name of the automatic transient class registration method. This will be set to the constant <see cref="NetConstants.DefaultAutomaticTransientClassRegistrationMethodName"/> this can be overwritten with a custom method name. </param>
        /// <returns>Fully formatted source code for registration class.</returns>
        public static string BuildNewRegistrationClass(VsProject sourceProject,
                                                       string className = NetConstants.DefaultDependencyInjectionClassName,
                                                       string automatedRegistrationMethodName = NetConstants.DefaultAutomaticTransientClassRegistrationMethodName)
        {
            if (sourceProject == null)
            {
                return(null);
            }

            if (!sourceProject.IsLoaded)
            {
                return(null);
            }

            string defaultNamespace = sourceProject.DefaultNamespace;

            if (string.IsNullOrEmpty(defaultNamespace))
            {
                return(null);
            }


            CodeFactory.SourceFormatter classFormatter = new CodeFactory.SourceFormatter();

            classFormatter.AppendCodeLine(0, "using System;");
            classFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
            classFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Configuration;");
            classFormatter.AppendCodeLine(0, "using Microsoft.Extensions.DependencyInjection;");
            classFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}");
            classFormatter.AppendCodeLine(0, "{");
            classFormatter.AppendCodeLine(1, "/// <summary>");
            classFormatter.AppendCodeLine(1, $"/// Responsible for dependency inject of services for the library <see cref=\"{sourceProject.Name}\"/>");
            classFormatter.AppendCodeLine(1, "/// </summary>");
            classFormatter.AppendCodeLine(1, $"public static class {className}");
            classFormatter.AppendCodeLine(1, "{");
            classFormatter.AppendCodeLine(2, "/// <summary>");
            classFormatter.AppendCodeLine(2, "/// Flag that determines if registration has already been performed on the library");
            classFormatter.AppendCodeLine(2, "/// </summary>");
            classFormatter.AppendCodeLine(2, "public static bool ServicesRegistered { get; private set; }");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(2, "/// <summary>");
            classFormatter.AppendCodeLine(2, "/// Register the dependency injection service that are supported by this library and triggers registration for other libraries referenced by this library.");
            classFormatter.AppendCodeLine(2, "/// </summary>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"serviceCollection\">The service collection that dependency injection uses.</param>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"configuration\">The hosting systems configuration.</param>");
            classFormatter.AppendCodeLine(2, "public static void RegisterServices(IServiceCollection serviceCollection, IConfiguration configuration)");
            classFormatter.AppendCodeLine(2, "{");
            classFormatter.AppendCodeLine(3, "//If services have already been registered do no process service registration. This protects from duplicate registration.");
            classFormatter.AppendCodeLine(3, "if (ServicesRegistered) return;");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(3, "//Call down stream libraries and have them complete their registration.");
            classFormatter.AppendCodeLine(3, "RegisterDependentServices(serviceCollection, configuration);");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(3, "//Process all manually managed registrations for this library.");
            classFormatter.AppendCodeLine(3, "ManualRegistrationServices(serviceCollection, configuration);");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(3, "//Process all automatically discovered services for registration.");
            classFormatter.AppendCodeLine(3, $"{automatedRegistrationMethodName}(serviceCollection);");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(3, "//Update the registration status.");
            classFormatter.AppendCodeLine(3, "ServicesRegistered = true;");
            classFormatter.AppendCodeLine(2, "}");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(2, "/// <summary>");
            classFormatter.AppendCodeLine(2, "/// Register dependency injection services for child libraries referenced by this library.");
            classFormatter.AppendCodeLine(2, "/// </summary>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"serviceCollection\">The service collection that dependency injection uses.</param>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"configuration\">The hosting systems configuration.</param>");
            classFormatter.AppendCodeLine(2, "private static void RegisterDependentServices(IServiceCollection serviceCollection, IConfiguration configuration)");
            classFormatter.AppendCodeLine(2, "{");
            classFormatter.AppendCodeLine(3, "//Todo: Register services from other libraries directly referenced by this library.");
            classFormatter.AppendCodeLine(2, "}");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(2, "/// <summary>");
            classFormatter.AppendCodeLine(2, "/// Register services with the service collection manually. This is where manual singleton objects and complex service registration is managed.");
            classFormatter.AppendCodeLine(2, "/// </summary>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"serviceCollection\">The service collection that dependency injection uses.</param>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"configuration\">The hosting systems configuration.</param>");
            classFormatter.AppendCodeLine(2, "private static void ManualRegistrationServices(IServiceCollection serviceCollection, IConfiguration configuration)");
            classFormatter.AppendCodeLine(2, "{");
            classFormatter.AppendCodeLine(3, "//TODO: manually add singleton and manually managed registrations here.");
            classFormatter.AppendCodeLine(2, "}");
            classFormatter.AppendCodeLine(0);
            classFormatter.AppendCodeLine(2, "/// <summary>");
            classFormatter.AppendCodeLine(2, "/// Automated registration of classes using transient registration.");
            classFormatter.AppendCodeLine(2, "/// </summary>");
            classFormatter.AppendCodeLine(2, "/// <param name=\"serviceCollection\">The service collection to register services.</param>");
            classFormatter.AppendCodeLine(2, $"private static void {automatedRegistrationMethodName}(IServiceCollection serviceCollection)");
            classFormatter.AppendCodeLine(2, "{");
            classFormatter.AppendCodeLine(3, "//Will be updated through code automation do not change or add to this method by hand.");
            classFormatter.AppendCodeLine(2, "}");
            classFormatter.AppendCodeLine(1, "}");
            classFormatter.AppendCodeLine(0, "}");

            return(classFormatter.ReturnSource());
        }
Example #7
0
        /// <summary>
        /// Generates the source code for a method that supports a standard type catch block, and bounds checking as well as the ability to support Microsoft extensions logging.
        /// </summary>
        /// <param name="memberData">Method data to be generated.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <param name="includeBoundsCheck">Flag that determines if string and nullable type bounds checking should be included in a method implementation.</param>
        /// <param name="supportAsyncKeyword">Flag that determines if methods should be implemented with the async keyword when supported by the method implementation.</param>
        /// <param name="security">The security level to add to the generated source code. Will default to public if not provided</param>
        /// <param name="includeLogging">Determines if logging will be added to the method. This will be set to a default of true.</param>
        /// <param name="loggerName">Sets the name of the logger to be used in the method. Will default to _logger if not provided.</param>
        /// <param name="includeKeywords">Determines if the models current keywords should be added to the source code, default if false.</param>
        /// <param name="includeAbstraction">If keywords are to be included, determines if the abstract keyword should be used or ignored, default is false.</param>
        /// <param name="requireStaticKeyword">Adds the static keyword to the signature, default is false.</param>
        /// <param name="requireSealedKeyword">Adds the sealed keyword to the signature, default is false.</param>
        /// <param name="requireAbstractKeyword">Adds the abstract keyword to the signature, default is false.</param>
        /// <param name="requireOverrideKeyword">Adds the override keyword to the signature, default is false.</param>
        /// <param name="requireVirtualKeyword">Adds the virtual keyword to the signature, default is false.</param>
        /// <returns>The fully formatted method source code or null if the member could not be implemented.</returns>
        public static string GenerateStandardMethodSourceCode(CsMethod memberData, NamespaceManager manager, bool includeBoundsCheck, bool supportAsyncKeyword,
                                                              CsSecurity security         = CsSecurity.Public, bool includeLogging = true, string loggerName          = null, bool includeKeywords = false,
                                                              bool includeAbstraction     = false, bool requireStaticKeyword       = false, bool requireSealedKeyword = false, bool requireAbstractKeyword = false,
                                                              bool requireOverrideKeyword = false, bool requireVirtualKeyword      = false)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            string logger = null;

            if (includeLogging)
            {
                logger = string.IsNullOrEmpty(loggerName) ? NetConstants.DefaultClassLoggerName : loggerName;
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //Using the formatter helper to generate a method signature.
            string methodSyntax = supportAsyncKeyword
                ? memberData.CSharpFormatMethodSignature(manager, true, true, security, includeKeywords,
                                                         includeAbstraction, requireStaticKeyword, requireSealedKeyword, requireAbstractKeyword,
                                                         requireOverrideKeyword, requireVirtualKeyword)
                : memberData.CSharpFormatMethodSignature(manager, false, true, security, includeKeywords,
                                                         includeAbstraction, requireStaticKeyword, requireSealedKeyword,
                                                         requireAbstractKeyword, requireOverrideKeyword, requireVirtualKeyword);

            //If the method syntax was not created return.
            if (string.IsNullOrEmpty(methodSyntax))
            {
                return(null);
            }

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }

            //Adding the method declaration
            formatter.AppendCodeLine(0, methodSyntax);
            formatter.AppendCodeLine(0, "{");

            //Adding enter logging if logging is enabled.
            if (includeLogging)
            {
                formatter.AppendCodeLine(1, $"{logger}.LogInformation(\"Entering\");");
                formatter.AppendCodeLine(0);
            }

            //Processing each parameter for bounds checking if bounds checking is enabled.
            if (includeBoundsCheck & memberData.HasParameters)
            {
                foreach (ICsParameter paramData in memberData.Parameters)
                {
                    //If the parameter has a default value then continue will not bounds check parameters with a default value.
                    if (paramData.HasDefaultValue)
                    {
                        continue;
                    }

                    //If the parameter is a string type add the following bounds check
                    if (paramData.ParameterType.WellKnownType == CsKnownLanguageType.String)
                    {
                        //Adding an if check
                        formatter.AppendCodeLine(1, $"if(string.IsNullOrEmpty({paramData.Name}))");
                        formatter.AppendCodeLine(1, "{");

                        //If logging was included add error logging and exit logging
                        if (includeLogging)
                        {
                            formatter.AppendCodeLine(2,
                                                     $"{logger}.LogError($\"The parameter {{nameof({paramData.Name})}} was not provided. Will raise an argument exception\");");
                            formatter.AppendCodeLine(2, $"{logger}.LogInformation(\"Exiting\");");
                        }
                        //Adding a throw of an argument null exception
                        formatter.AppendCodeLine(2, $"throw new ArgumentNullException(nameof({paramData.Name}));");
                        formatter.AppendCodeLine(1, "}");
                        formatter.AppendCodeLine(0);
                    }

                    // Check to is if the parameter is not a value type or a well know type if not then go ahead and perform a null bounds check.
                    if (!paramData.ParameterType.IsValueType & !paramData.ParameterType.IsWellKnownType)
                    {
                        //Adding an if check
                        formatter.AppendCodeLine(1, $"if({paramData.Name} == null)");
                        formatter.AppendCodeLine(1, "{");

                        //If logging was included add error logging and exit logging
                        if (includeLogging)
                        {
                            formatter.AppendCodeLine(2,
                                                     $"{logger}.LogError($\"The parameter {{nameof({paramData.Name})}} was not provided. Will raise an argument exception\");");
                            formatter.AppendCodeLine(2, $"{logger}.LogInformation(\"Exiting\");");
                        }

                        //Adding a throw of an argument null exception
                        formatter.AppendCodeLine(2, $"throw new ArgumentNullException(nameof({paramData.Name}));");
                        formatter.AppendCodeLine(1, "}");
                        formatter.AppendCodeLine(0);
                    }
                }
            }

            //Formatting standard try block for method
            formatter.AppendCodeLine(1, "try");
            formatter.AppendCodeLine(1, "{");
            formatter.AppendCodeLine(2, "//TODO: add execution logic here");
            formatter.AppendCodeLine(1, "}");

            //Formatting standard catch block for method
            formatter.AppendCodeLine(1, "catch (Exception unhandledException)");
            formatter.AppendCodeLine(1, "{");

            //If logging is supported capture the exception and log it as an error. Notify that an error has occured and log it . then log exiting the method and throw a scrubbed exception.
            if (includeLogging)
            {
                formatter.AppendCodeLine(2, $"{logger}.LogError(unhandledException, \"An unhandled exception occured, see the exception for details.Will throw a UnhandledLogicException\");");
                formatter.AppendCodeLine(2, $"{logger}.LogInformation(\"Exiting\");");
                formatter.AppendCodeLine(2, "throw new Exception(\"An unhandled error occured, check the logs for details.\");");
            }
            else
            {
                formatter.AppendCodeLine(2, "//TODO: Add exception handling for unhandledException");
            }


            formatter.AppendCodeLine(1, "}");
            formatter.AppendCodeLine(0);

            //If logging add a logging exit statement.
            if (includeLogging)
            {
                formatter.AppendCodeLine(1, $"{logger}.LogInformation(\"Exiting\");");
            }

            //Add an exception for not implemented until the developer updates the logic.
            formatter.AppendCodeLine(1, "throw new NotImplementedException();");
            formatter.AppendCodeLine(0);

            //if the return type is not void then add a to do message for the developer to add return logic.
            if (!memberData.IsVoid)
            {
                formatter.AppendCodeLine(1, "//TODO: add return logic here");
            }
            formatter.AppendCodeLine(0, "}");
            formatter.AppendCodeLine(0);

            //Returning the fully formatted method.
            return(formatter.ReturnSource());
        }
Example #8
0
        /// <summary>
        /// Generates the source code for an event that supports add remove, does not generate the field definition.
        /// </summary>
        /// <param name="memberData">Event data to be loaded.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <param name="fieldCamelCase">Flag that determines if the field will be set to camel case or left in proper case.</param>
        /// <param name="security">The security level to set the source event source code to. Will default to public if not provided.</param>
        /// <param name="useKeywords">Include the keywords currently assigned to the event model. Default value is to not include them.</param>
        /// <param name="includeAbstractKeyword">Optional parameter that determines if it will include the definition for the abstract keyword in the definition if it is defined. default is false.</param>
        /// <param name="requireStaticKeyword">Adds the static keyword to the signature, default is false.</param>
        /// <param name="requireSealedKeyword">Adds the sealed keyword to the signature, default is false.</param>
        /// <param name="requireAbstractKeyword">Adds the abstract keyword to the signature, default is false.</param>
        /// <param name="requireOverrideKeyword">Adds the override keyword to the signature, default is false.</param>
        /// <param name="requireVirtualKeyword">Adds the virtual keyword to the signature, default is false.</param>
        /// <param name="fieldNamePrefix">Optional parameter that determines the prefix to add to the field name that backs the event.</param>
        /// <param name="fieldSuffix">Parameter that determines what the suffix of the event name field is. Default is handler.</param>
        /// <returns>The fully formatted event source code or null if the member could not be implemented.</returns>
        public static string GenerateAddRemoveEvent(CsEvent memberData, NamespaceManager manager = null, CsSecurity security = CsSecurity.Public, string fieldNamePrefix = null, string fieldSuffix = "Handler", bool fieldCamelCase = true,
                                                    bool useKeywords            = false, bool includeAbstractKeyword = false, bool requireStaticKeyword  = false, bool requireSealedKeyword = false,
                                                    bool requireAbstractKeyword = false, bool requireOverrideKeyword = false, bool requireVirtualKeyword = false)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }


            var eventSignature = memberData.CSharpFormatEventSignature(manager, true, security, useKeywords,
                                                                       includeAbstractKeyword, requireStaticKeyword, requireSealedKeyword, requireAbstractKeyword,
                                                                       requireOverrideKeyword, requireVirtualKeyword);

            if (string.IsNullOrEmpty(eventSignature))
            {
                return(null);
            }

            bool hasFieldPrefix = fieldNamePrefix != null;

            bool hasFieldSuffix = fieldSuffix != null;

            StringBuilder fieldNameBuilder = new StringBuilder();

            if (hasFieldPrefix)
            {
                fieldNameBuilder.Append(fieldNamePrefix);
            }

            var fieldNameData = hasFieldSuffix ? $"{memberData.Name}{fieldSuffix}": memberData.Name;

            fieldNameBuilder.Append(fieldCamelCase ? fieldNameData.ConvertToCamelCase() : fieldNameData);

            var fieldName = fieldNameBuilder.ToString();

            //Adding the event declaration
            formatter.AppendCodeLine(0, eventSignature);

            formatter.AppendCodeLine(0);
            formatter.AppendCodeLine(0, "{");
            formatter.AppendCodeLine(0);
            formatter.AppendCodeLine(1, "add");
            formatter.AppendCodeLine(1, "{");
            formatter.AppendCodeLine(2, $"{fieldName} += value;");
            formatter.AppendCodeLine(1, "}");
            formatter.AppendCodeLine(0);
            formatter.AppendCodeLine(1, "remove");
            formatter.AppendCodeLine(1, "{");
            formatter.AppendCodeLine(2, $"if({fieldName} !=null) {fieldName} -= value;");
            formatter.AppendCodeLine(1, "}");
            formatter.AppendCodeLine(0);
            formatter.AppendCodeLine(0, "}");
            formatter.AppendCodeLine(0);

            //The source formatter returning the final results.
            return(formatter.ReturnSource());
        }
Example #9
0
        /// <summary>
        /// Generates the source code for a standard field definition.
        /// </summary>
        /// <param name="memberData">Event data to be loaded.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <param name="fieldSecurity">Parameter to set the target security for the field.</param>
        /// <param name="includeKeywords">Optional parameter that will include all keywords assigned to the field from the source model. This is true by default.</param>
        /// <param name="implementConstant">Determines if the filed is implemented as constant is should be returned as a constant, default is true.</param>
        /// <param name="requireStaticKeyword">Adds the static keyword to the signature, default is false.</param>
        /// <param name="requireReadOnlyKeyword">Adds the readonly keyword to the signature, default is false.</param>
        /// <param name="requireConstant">Implements the field as a constant, default is false.</param>
        /// <param name="requireConstantValue">The value to set the constant to if required.</param>
        /// <returns>The fully formatted event source code or null if the member could not be implemented.</returns>
        public static string GenerateStandardFieldSourceCode(CsField memberData, NamespaceManager manager,
                                                             CsSecurity fieldSecurity, bool includeKeywords = true, bool implementConstant = true,
                                                             bool requireStaticKeyword   = false, bool requireReadOnlyKeyword = false, bool requireConstant = false,
                                                             string requireConstantValue = null)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //Using the formatter helper to generate a default event signature.
            string fieldSyntax = memberData.CSharpFormatFieldDeclaration(manager, includeKeywords, fieldSecurity,
                                                                         implementConstant, requireStaticKeyword, requireReadOnlyKeyword, requireConstant, requireConstantValue);

            //If the property syntax was not created return.
            if (string.IsNullOrEmpty(fieldSyntax))
            {
                return(null);
            }

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }

            //Adding the event declaration
            formatter.AppendCodeBlock(0, fieldSyntax);

            //Adding a extra line feed at the end of the declaration.
            formatter.AppendCodeLine(0);

            //The source formatter returning the final results.
            return(formatter.ReturnSource());
        }
Example #10
0
        /// <summary>
        /// Generates the source code for a standard property definition. That uses a standard getter and setter.
        /// </summary>
        /// <param name="memberData">property data to be loaded.</param>
        /// <param name="manager">The namespace manager to use for namespace management with type declarations.</param>
        /// <param name="security">The security level to set the source event source code to. Will default to public if not provided.</param>
        /// <param name="getSecurity">Set the security level of the get accessor if defined for the property, will default to unknown</param>
        /// <param name="setSecurity">Set the security level of the set accessor if defined for the property, will default to unknown</param>
        /// <param name="useKeywords">Include the keywords currently assigned to the event model. Default value is to not include them.</param>
        /// <param name="includeAbstractKeyword">Optional parameter that determines if it will include the definition for the abstract keyword in the definition if it is defined. default is false.</param>
        /// <param name="requireStaticKeyword">Adds the static keyword to the signature, default is false.</param>
        /// <param name="requireSealedKeyword">Adds the sealed keyword to the signature, default is false.</param>
        /// <param name="requireAbstractKeyword">Adds the abstract keyword to the signature, default is false.</param>
        /// <param name="requireOverrideKeyword">Adds the override keyword to the signature, default is false.</param>
        /// <param name="requireVirtualKeyword">Adds the virtual keyword to the signature, default is false.</param>
        /// <returns>The fully formatted event source code or null if the member could not be implemented.</returns>
        public static string GenerateStandardPropertySourceCode(CsProperty memberData, NamespaceManager manager,
                                                                bool useKeywords           = false, bool includeAbstractKeyword         = false, bool requireStaticKeyword   = false,
                                                                bool requireSealedKeyword  = false, bool requireAbstractKeyword         = false, bool requireOverrideKeyword = false,
                                                                bool requireVirtualKeyword = false, CsSecurity security                 = CsSecurity.Public,
                                                                CsSecurity getSecurity     = CsSecurity.Unknown, CsSecurity setSecurity = CsSecurity.Unknown)
        {
            //Bounds checking to make sure all data that is needed is provided. If any required data is missing will return null.
            if (memberData == null)
            {
                return(null);
            }
            if (!memberData.IsLoaded)
            {
                return(null);
            }
            if (manager == null)
            {
                return(null);
            }

            //C# helper used to format output syntax.
            var formatter = new CodeFactory.SourceFormatter();

            //Using the formatter helper to generate a default event signature.
            string propertySyntax = memberData.CSharpFormatDefaultPropertySignature(manager, useKeywords,
                                                                                    includeAbstractKeyword, requireStaticKeyword, requireSealedKeyword, requireAbstractKeyword,
                                                                                    requireOverrideKeyword, requireVirtualKeyword, security, setSecurity, getSecurity);

            //If the property syntax was not created return.
            if (string.IsNullOrEmpty(propertySyntax))
            {
                return(null);
            }

            //If the member has document then will build the documentation.
            if (memberData.HasDocumentation)
            {
                //Using a documentation helper that will generate an enumerator that will output all XML documentation for the member.
                foreach (var documentation in memberData.CSharpFormatXmlDocumentationEnumerator())
                {
                    //Appending each xml document line to the being of the member definition.
                    formatter.AppendCodeLine(0, documentation);
                }
            }

            //The member has attributes assigned to it, append the attributes.
            if (memberData.HasAttributes)
            {
                //Using a documentation helper that will generate an enumerator that will output each attribute definition.
                foreach (var attributeSyntax in memberData.Attributes.CSharpFormatAttributeDeclarationEnumerator(manager))
                {
                    //Appending each attribute definition before the member definition.
                    formatter.AppendCodeLine(0, attributeSyntax);
                }
            }

            //Adding the event declaration
            formatter.AppendCodeBlock(0, propertySyntax);

            //Adding a extra line feed at the end of the declaration.
            formatter.AppendCodeLine(0);

            //The source formatter returning the final results.
            return(formatter.ReturnSource());
        }