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