/// <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);
        }
Ejemplo n.º 2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="targetClass"></param>
        /// <param name="sourceCode"></param>
        /// <param name="members"></param>
        /// <param name="logging"></param>
        /// <param name="cdf"></param>
        /// <param name="cdfAspnet"></param>
        /// <param name="isContract"></param>
        /// <returns></returns>
        private async Task <CsSource> UpdateFileAsync(CsClass targetClass, CsSource sourceCode, IEnumerable <CsMember> members, bool logging, bool cdf, bool cdfAspnet, bool isContract)
        {
            if (targetClass == null)
            {
                throw new CodeFactoryException("Cannot access class data cannot add members");
            }
            if (sourceCode == null)
            {
                throw new CodeFactoryException("Cannot access the classes source code cannot add members.");
            }
            if (members == null)
            {
                return(sourceCode);
            }
            if (!members.Any())
            {
                return(sourceCode);
            }

            CsSource updatedSourceCode = await sourceCode.AddMissingNamespaces(members, targetClass.Namespace);

            CsClass updatedClass = targetClass;



            if (logging)
            {
                updatedSourceCode = await updatedSourceCode.AddUsingStatementAsync(NetConstants.MicrosoftLoggerNamespace);

                updatedClass = sourceCode.GetModel(updatedClass.LookupPath) as CsClass;
                if (updatedClass == null)
                {
                    throw new CodeFactoryException("Cannot get class data to add members.");
                }

                if (!updatedClass.Fields.Any(f => f.Name == NetConstants.DefaultClassLoggerName))
                {
                    var formatter = new SourceFormatter();
                    formatter.AppendCodeLine(0, "///<summary>");
                    formatter.AppendCodeLine(0, "///Logger used to manage logging for this class.");
                    formatter.AppendCodeLine(0, "///</summary>");
                    formatter.AppendCodeLine(0, $"private ILogger {NetConstants.DefaultClassLoggerName};");
                    updatedSourceCode =
                        await updatedClass.AddToBeginningAsync(
                            CsSourceFormatter.IndentCodeBlock(2, formatter.ReturnSource()));
                }
            }

            if (cdf)
            {
                updatedSourceCode = await updatedSourceCode.AddUsingStatementAsync(CommonDeliveryFrameworkConstants
                                                                                   .CommonDeliveryFrameworkNamespace);
            }

            if (cdfAspnet)
            {
                updatedSourceCode = await updatedSourceCode.AddUsingStatementAsync(CommonDeliveryFrameworkConstants
                                                                                   .CommonDeliveryFrameworkNetAspNetNamespace);
            }

            updatedClass = sourceCode.GetModel(updatedClass.LookupPath) as CsClass;
            if (updatedClass == null)
            {
                throw new CodeFactoryException("Cannot get class data to add members.");
            }

            updatedSourceCode = await UpdateMembersAsync(updatedClass, members, logging, cdf, cdfAspnet,
                                                         updatedSourceCode.SourceDocument, isContract, sourceCode.LoadNamespaceManager(updatedClass.Namespace));

            return(updatedSourceCode);
        }
        /// <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);
        }