Exemplo n.º 1
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);
        }
Exemplo n.º 2
0
        private async Task <CsSource> UpdateSubscriptionAsync(CsField subscriptionField, CsClass sourceClass, VsCSharpSource source)
        {
            SourceFormatter formatter        = new SourceFormatter();
            string          injectSourceCode = null;

            var contract = subscriptionField.DataType.GetInterfaceModel();

            if (contract == null)
            {
                return(null);
            }

            CsSource sourceCode = source.SourceCode;

            try
            {
                CsClass currentClass = sourceClass;

                var events = contract.Events;

                var subscribePath = ContractHelper.GetSubscribeFilePath(currentClass);


                if (!subscribePath.hasFile)
                {
                    var manager = sourceCode.LoadNamespaceManager(sourceClass.Namespace);

                    var parent = await source.GetParentAsync();

                    if (parent == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }

                    VsDocument generatedDocument  = null;
                    string     partialClassSource = CSharpSourceGenerationCommon.GeneratePartialClass(currentClass, manager);
                    if (parent.ModelType == VisualStudioModelType.ProjectFolder)
                    {
                        var parentFolder = parent as VsProjectFolder;

                        if (parentFolder == null)
                        {
                            throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                        }

                        generatedDocument = await parentFolder.AddDocumentAsync(Path.GetFileName(subscribePath.filePath),
                                                                                partialClassSource);
                    }
                    else
                    {
                        var parentProject = parent as VsProject;

                        if (parentProject == null)
                        {
                            throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                        }

                        generatedDocument = await parentProject.AddDocumentAsync(subscribePath.filePath,
                                                                                 partialClassSource);
                    }
                    sourceCode = await generatedDocument.GetCSharpSourceModelAsync();

                    sourceCode = await sourceCode.AddMissingNamespaces(contract.Events, currentClass.Namespace);

                    currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;

                    if (currentClass == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }
                }
                else
                {
                    var parent = await source.GetParentAsync();

                    VsCSharpSource sourceDocument = null;
                    if (parent.ModelType == VisualStudioModelType.ProjectFolder)
                    {
                        var parentFolder = parent as VsProjectFolder;

                        if (parentFolder == null)
                        {
                            throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                        }

                        var children = await parentFolder.GetChildrenAsync(false, true);

                        sourceDocument = children.Where(c => c.ModelType == VisualStudioModelType.CSharpSource)
                                         .Cast <VsCSharpSource>()
                                         .FirstOrDefault(s => s.SourceCode.SourceDocument == subscribePath.filePath);
                    }
                    else
                    {
                        var parentProject = parent as VsProject;

                        if (parentProject == null)
                        {
                            throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                        }

                        var children = await parentProject.GetChildrenAsync(false, true);

                        sourceDocument = children.Where(c => c.ModelType == VisualStudioModelType.CSharpSource)
                                         .Cast <VsCSharpSource>()
                                         .FirstOrDefault(s => s.SourceCode.SourceDocument == subscribePath.filePath);;
                    }

                    if (sourceDocument == null)
                    {
                        throw new CodeFactoryException("Could load the contract document.");
                    }

                    sourceCode = sourceDocument.SourceCode;

                    sourceCode = await sourceCode.AddMissingNamespaces(contract.Events, currentClass.Namespace);

                    currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;
                    if (currentClass == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }
                }

                var namespaceManager = sourceCode.LoadNamespaceManager(currentClass.Namespace);

                foreach (var contractEvent in contract.Events)
                {
                    var eventHandlerName =
                        CSharpSourceGenerationWPF.GenerateContractEventHandlerMethodName(subscriptionField, contractEvent);

                    if (eventHandlerName == null)
                    {
                        throw new CodeFactoryException($"Could not create the source code for a contract event handler.");
                    }

                    if (currentClass.Methods.Any(m => m.Name == eventHandlerName))
                    {
                        continue;
                    }

                    var eventHandlerSource = CSharpSourceGenerationWPF.GenerateContractEventHandlerMethod(subscriptionField, contractEvent,
                                                                                                          namespaceManager);

                    if (eventHandlerSource == null)
                    {
                        throw new CodeFactoryException($"Could not create the source code for the event handler {eventHandlerName}");
                    }

                    sourceCode = await currentClass.AddToEndAsync(subscribePath.filePath, InjectSourceCodeAtLevel(2, eventHandlerSource));

                    currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;
                    if (currentClass == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }
                }

                var subscriptionName   = CSharpSourceGenerationWPF.GenerateContractSubscriptionMethodName(subscriptionField);
                var subscriptionMethod = currentClass.Methods.FirstOrDefault(m => m.Name == subscriptionName);

                if (subscriptionMethod != null)
                {
                    sourceCode = await subscriptionMethod.DeleteAsync();

                    currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;
                    if (currentClass == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }
                }

                var subscriptionSource = CSharpSourceGenerationWPF.GenerateContractSubscriptionMethod(subscriptionField, contract);

                if (subscriptionSource == null)
                {
                    throw new CodeFactoryException("Cannot generate the subscription contract source code.");
                }

                sourceCode = await currentClass.AddToEndAsync(subscribePath.filePath, InjectSourceCodeAtLevel(2, subscriptionSource));

                currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;
                if (currentClass == null)
                {
                    throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                }

                var releaseName   = CSharpSourceGenerationWPF.GenerateContractReleaseMethodName(subscriptionField);
                var releaseMethod = currentClass.Methods.FirstOrDefault(m => m.Name == releaseName);

                if (releaseMethod != null)
                {
                    sourceCode = await releaseMethod.DeleteAsync();

                    currentClass = sourceCode.GetModel(currentClass.LookupPath) as CsClass;
                    if (currentClass == null)
                    {
                        throw new CodeFactoryException("Cannot access the parent of the source code document, cannot update subscription");
                    }
                }

                var releaseSource = CSharpSourceGenerationWPF.GenerateContractReleaseMethod(subscriptionField, contract);

                if (releaseSource == null)
                {
                    throw new CodeFactoryException("Cannot generate the release contract source code.");
                }

                sourceCode = await currentClass.AddToEndAsync(subscribePath.filePath, InjectSourceCodeAtLevel(2, releaseSource));
            }
            catch (CodeFactoryException)
            {
                throw;
            }
            catch (Exception unhandledException)
            {
                throw new CodeFactoryException("The following unhandledException occured", unhandledException);
            }

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