public ClassDeclarationSyntax GeneratePartialController(INamedTypeSymbol controllerSymbol, string areaKey, string areaName, string controllerName, string projectRoot) { // build controller partial class node // add a default constructor if there are some but none are zero length var genControllerClass = SyntaxNodeHelpers.CreateClass( controllerSymbol.Name, controllerSymbol.TypeParameters.Select(tp => TypeParameter(tp.Name)).ToArray(), SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword); var gotCustomConstructors = controllerSymbol.Constructors .Where(c => c.DeclaredAccessibility == Accessibility.Public) .Where(SyntaxNodeHelpers.IsNotR4MVCGenerated) .Where(c => !c.IsImplicitlyDeclared) .Any(); if (!gotCustomConstructors) { genControllerClass = genControllerClass.WithDefaultConstructor(true, SyntaxKind.PublicKeyword); } genControllerClass = genControllerClass.WithDummyConstructor(true, SyntaxKind.ProtectedKeyword); genControllerClass = AddRedirectMethods(genControllerClass); // add all method stubs, TODO criteria for this: only public virtual actionresults? // add subclasses, fields, properties, constants for action names genControllerClass = AddParameterlessMethods(genControllerClass, controllerSymbol); var actionsExpression = !string.IsNullOrEmpty(areaKey) ? SyntaxNodeHelpers.MemberAccess(_settings.HelpersPrefix + "." + areaKey, controllerName) : SyntaxNodeHelpers.MemberAccess(_settings.HelpersPrefix, controllerName); genControllerClass = genControllerClass .WithProperty("Actions", controllerSymbol.Name, actionsExpression, SyntaxKind.PublicKeyword) .WithStringField( "Area", areaName, true, SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword) .WithStringField( "Name", controllerName, true, SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword) .WithStringField( "NameConst", controllerName, true, SyntaxKind.PublicKeyword, SyntaxKind.ConstKeyword) .WithField("s_actions", "ActionNamesClass", SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword) .WithProperty("ActionNames", "ActionNamesClass", IdentifierName("s_actions"), SyntaxKind.PublicKeyword) .WithActionNameClass(controllerSymbol) .WithActionConstantsClass(controllerSymbol) .WithViewsClass(controllerName, areaName, _viewLocator.FindViews(projectRoot)); return(genControllerClass); }
public void Generate(CSharpCompilation compilation, string projectRoot) { // create static MVC class and add controller fields var mvcStaticClass = SyntaxNodeHelpers.CreateClass(_settings.HelpersPrefix, null, SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword) .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute()); // R4MVC namespace used for the areas and Dummy class var r4Namespace = SyntaxNodeHelpers.CreateNamespace(_settings.R4MvcNamespace) // add the dummy class using in the derived controller partial class .WithDummyClass(); var areaClasses = new Dictionary <string, ClassDeclarationSyntax>(); var controllers = _controllerRewriter.RewriteControllers(compilation, R4MvcFileName); var controllerDetails = controllers .Where(c => !c.SyntaxTree.FilePath.EndsWith(".generated.cs")) .Select(c => { var @namespace = c.Ancestors().OfType <NamespaceDeclarationSyntax>().First().Name.ToFullString().Trim(); var model = compilation.GetSemanticModel(c.SyntaxTree); var controllerSymbol = model.GetDeclaredSymbol(c); var controllerName = controllerSymbol.Name.TrimEnd("Controller"); var areaName = _controllerGenerator.GetControllerArea(controllerSymbol); return(new { FilePath = c.SyntaxTree.FilePath, Namespace = @namespace, Name = controllerName, Area = areaName, Symbol = controllerSymbol, }); }); var namespaceGroups = controllerDetails.GroupBy(c => c.Namespace); var rootControllerNames = controllerDetails.Where(c => string.IsNullOrEmpty(c.Area)).Select(c => c.Name).ToArray(); var allViewFiles = _viewLocator.FindViews(projectRoot); var generatedControllers = new List <NamespaceDeclarationSyntax>(); foreach (var nameGroup in namespaceGroups) { var namespaceNode = SyntaxNodeHelpers.CreateNamespace(nameGroup.Key); foreach (var controller in nameGroup) { var areaKey = rootControllerNames.Contains(controller.Area) ? controller.Area + "Area" : controller.Area; var genControllerClass = _controllerGenerator.GeneratePartialController(controller.Symbol, areaKey, controller.Area, controller.Name, projectRoot); var r4ControllerClass = _controllerGenerator.GenerateR4Controller(controller.Symbol); namespaceNode = namespaceNode .AddMembers(genControllerClass, r4ControllerClass); if (_settings.SplitIntoMutipleFiles) { var controllerFile = NewCompilationUnit() .AddMembers(namespaceNode); CompleteAndWriteFile(controllerFile, controller.FilePath.TrimEnd(".cs") + ".generated.cs"); namespaceNode = SyntaxNodeHelpers.CreateNamespace(nameGroup.Key); } if (!string.IsNullOrEmpty(controller.Area)) { if (!areaClasses.ContainsKey(controller.Area)) { string areaClass = controller.Area + "AreaClass"; areaClasses[controller.Area] = SyntaxNodeHelpers.CreateClass(areaClass, null, SyntaxKind.PublicKeyword); // change to field and property mvcStaticClass = mvcStaticClass.AddMembers( SyntaxNodeHelpers.CreateFieldWithDefaultInitializer("s_" + areaKey, areaClass, SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword), SyntaxNodeHelpers.CreateProperty(areaKey, areaClass, IdentifierName("s_" + areaKey), SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword)); } areaClasses[controller.Area] = areaClasses[controller.Area].AddMembers( SyntaxNodeHelpers.CreateFieldWithDefaultInitializer( controller.Name, $"{nameGroup.Key}.{genControllerClass.Identifier}", $"{nameGroup.Key}.{r4ControllerClass.Identifier}", SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword)); } else { mvcStaticClass = mvcStaticClass.AddMembers( SyntaxNodeHelpers.CreateFieldWithDefaultInitializer( controller.Name, $"{nameGroup.Key}.{genControllerClass.Identifier}", $"{nameGroup.Key}.{r4ControllerClass.Identifier}", SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword)); } } if (!_settings.SplitIntoMutipleFiles) { generatedControllers.Add(namespaceNode); } } var views = _viewLocator.FindViews(projectRoot) .GroupBy(v => new { v.AreaName, v.ControllerName }); foreach (var viewSet in views) { if (controllerDetails.Any(c => c.Area == viewSet.Key.AreaName && c.Name == viewSet.Key.ControllerName)) { continue; } var className = !string.IsNullOrEmpty(viewSet.Key.AreaName) ? $"{viewSet.Key.AreaName}Area_{viewSet.Key.ControllerName}ControllerClass" : $"{viewSet.Key.ControllerName}ControllerClass"; var controllerClass = SyntaxNodeHelpers.CreateClass(className, null, SyntaxKind.PublicKeyword) .WithViewsClass(viewSet.Key.ControllerName, viewSet.Key.AreaName, viewSet); r4Namespace = r4Namespace.AddMembers(controllerClass); if (!string.IsNullOrEmpty(viewSet.Key.AreaName)) { areaClasses[viewSet.Key.AreaName] = areaClasses[viewSet.Key.AreaName].AddMembers( SyntaxNodeHelpers.CreateFieldWithDefaultInitializer( viewSet.Key.ControllerName, $"{_settings.R4MvcNamespace}.{className}", $"{_settings.R4MvcNamespace}.{className}", SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword)); } else { mvcStaticClass = mvcStaticClass.AddMembers( SyntaxNodeHelpers.CreateFieldWithDefaultInitializer( viewSet.Key.ControllerName, $"{_settings.R4MvcNamespace}.{className}", $"{_settings.R4MvcNamespace}.{className}", SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword)); } } r4Namespace = r4Namespace.AddMembers(areaClasses.Values.ToArray()); var staticFileNode = _staticFileGenerator.GenerateStaticFiles(projectRoot); var actionResultClass = SyntaxNodeHelpers.CreateClass(Constants.ActionResultClass, null, SyntaxKind.InternalKeyword, SyntaxKind.PartialKeyword) .WithBaseTypes("ActionResult", "IR4MvcActionResult") .WithMethods(ConstructorDeclaration(Constants.ActionResultClass) .WithModifiers(SyntaxKind.PublicKeyword) .AddParameterListParameters( Parameter(Identifier("area")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("controller")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("action")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("protocol")).WithType(SyntaxNodeHelpers.PredefinedStringType()) .WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.NullLiteralExpression)))) .WithBody( Block( ExpressionStatement( InvocationExpression( SyntaxNodeHelpers.MemberAccess("this", "InitMVCT4Result")) .WithArgumentList( IdentifierName("area"), IdentifierName("controller"), IdentifierName("action"), IdentifierName("protocol")))))) .WithAutoStringProperty("Controller", SyntaxKind.PublicKeyword) .WithAutoStringProperty("Action", SyntaxKind.PublicKeyword) .WithAutoStringProperty("Protocol", SyntaxKind.PublicKeyword) .WithAutoProperty("RouteValueDictionary", IdentifierName("RouteValueDictionary"), SyntaxKind.PublicKeyword); var jsonResultClass = SyntaxNodeHelpers.CreateClass(Constants.JsonResultClass, null, SyntaxKind.InternalKeyword, SyntaxKind.PartialKeyword) .WithBaseTypes("JsonResult", "IR4MvcActionResult") .WithMethods(ConstructorDeclaration(Constants.JsonResultClass) .WithModifiers(SyntaxKind.PublicKeyword) .AddParameterListParameters( Parameter(Identifier("area")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("controller")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("action")).WithType(SyntaxNodeHelpers.PredefinedStringType()), Parameter(Identifier("protocol")).WithType(SyntaxNodeHelpers.PredefinedStringType()) .WithDefault(EqualsValueClause(LiteralExpression(SyntaxKind.NullLiteralExpression)))) .WithInitializer(ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, ArgumentList(SingletonSeparatedList(Argument(LiteralExpression(SyntaxKind.NullLiteralExpression)))))) .WithBody( Block( ExpressionStatement( InvocationExpression( SyntaxNodeHelpers.MemberAccess("this", "InitMVCT4Result")) .WithArgumentList( IdentifierName("area"), IdentifierName("controller"), IdentifierName("action"), IdentifierName("protocol")))))) .WithAutoStringProperty("Controller", SyntaxKind.PublicKeyword) .WithAutoStringProperty("Action", SyntaxKind.PublicKeyword) .WithAutoStringProperty("Protocol", SyntaxKind.PublicKeyword) .WithAutoProperty("RouteValueDictionary", IdentifierName("RouteValueDictionary"), SyntaxKind.PublicKeyword); var r4mvcNode = NewCompilationUnit() .AddMembers(generatedControllers.Cast <MemberDeclarationSyntax>().ToArray()) .AddMembers( staticFileNode, r4Namespace, mvcStaticClass, actionResultClass, jsonResultClass); CompleteAndWriteFile(r4mvcNode, Path.Combine(projectRoot, R4MvcGenerator.R4MvcFileName)); }
public IEnumerable <NamespaceDeclarationSyntax> GenerateControllers( CSharpCompilation compiler, IEnumerable <ClassDeclarationSyntax> controllerNodes) { // controllers might be in different namespaces so should group by namespace var namespaceGroups = controllerNodes.GroupBy(x => x.Ancestors().OfType <NamespaceDeclarationSyntax>().First().Name.ToFullString()); foreach (var namespaceControllers in namespaceGroups) { // create the namespace for the controllers var namespaceNode = SyntaxNodeHelpers.CreateNamespace(namespaceControllers.Key); // loop through the controllers and create a partial node for each foreach (var mvcControllerNode in namespaceControllers) { var model = compiler.GetSemanticModel(mvcControllerNode.SyntaxTree); var mvcSymbol = model.GetDeclaredSymbol(mvcControllerNode); // build controller partial class node // add a default constructor if there are some but none are zero length var genControllerClass = SyntaxNodeHelpers.CreateClass( mvcSymbol.Name, mvcControllerNode.TypeParameterList?.Parameters.ToArray(), SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword); if (!mvcSymbol.Constructors.IsEmpty || !mvcSymbol.Constructors.Any(x => x.Parameters.Length == 0)) { genControllerClass = genControllerClass.WithDefaultConstructor(true, SyntaxKind.PublicKeyword); } // add all method stubs, TODO criteria for this: only public virtual actionresults? // add subclasses, fields, properties, constants for action names genControllerClass = genControllerClass.WithMethods(mvcSymbol) .WithStringField( "Name", mvcControllerNode.Identifier.ToString(), true, SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword) .WithStringField( "NameConst", mvcControllerNode.Identifier.ToString(), true, SyntaxKind.PublicKeyword, SyntaxKind.ConstKeyword) .WithStringField( "Area", mvcControllerNode.Identifier.ToString(), true, SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword) .WithField("s_actions", "ActionNamesClass", SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword) .WithActionNameClass(mvcControllerNode) .WithActionConstantsClass(mvcControllerNode) .WithField("s_views", "ViewsClass", SyntaxKind.StaticKeyword, SyntaxKind.ReadOnlyKeyword) .WithViewsClass(_viewLocator.FindViews()); // create R4MVC_[Controller] class inheriting from partial // TODO chain base constructor call : base(Dummy.Instance) // TODO create [method]overrides(T4MVC_System_Web_Mvc_ActionResult callInfo) // TODO create method overrides that call above var r4ControllerClass = SyntaxNodeHelpers.CreateClass( GetR4MVCControllerClassName(genControllerClass), null, SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword) .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute()) .WithBaseTypes(mvcControllerNode.ToQualifiedName()) .WithDefaultConstructor(false, SyntaxKind.PublicKeyword); namespaceNode = namespaceNode.AddMembers(genControllerClass).AddMembers(r4ControllerClass); } yield return(namespaceNode); } }