Beispiel #1
0
        public SyntaxNode Generate(CompilationContext context, ISettings settings)
        {
            // Create the root node and add usings, header, pragma
            var r4mvcNode =
                SyntaxFactory.CompilationUnit()
                .WithUsings("System.CodeDom.Compiler", "System.Diagnostics", "Microsoft.AspNet.Mvc")
                .WithHeader(_headerText)
                .WithPragmaCodes(false, pramaCodes);

            var controllers          = _controllerRewriter.RewriteControllers(context.Compilation, R4MvcFileName);
            var generatedControllers = _controllerGenerator.GenerateControllers(context.Compilation, controllers).ToImmutableArray();
            var staticFileNode       = _staticFileGenerator.GenerateStaticFiles(settings);

            // add the dummy class using in the derived controller partial class
            var r4Namespace = SyntaxNodeHelpers.CreateNamespace(settings.R4MvcNamespace).WithDummyClass();

            // 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())
                .WithControllerFields(controllers);

            r4mvcNode =
                r4mvcNode.AddMembers(generatedControllers.Cast <MemberDeclarationSyntax>().ToArray())
                .AddMembers(staticFileNode)
                .AddMembers(r4Namespace)
                .AddMembers(mvcStaticClass)
                .NormalizeWhitespace()
                .WithPragmaCodes(true, pramaCodes);

            // NOTE reenable pragma codes after normalizing whitespace or it doesn't place on newline
            return(r4mvcNode);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        public ClassDeclarationSyntax GenerateR4Controller(INamedTypeSymbol controllerSymbol)
        {
            // create R4MVC_[Controller] class inheriting from partial
            var r4ControllerClass =
                SyntaxNodeHelpers.CreateClass(
                    GetR4MVCControllerClassName(controllerSymbol),
                    null,
                    SyntaxKind.PublicKeyword,
                    SyntaxKind.PartialKeyword)
                .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                .WithBaseTypes(controllerSymbol.ToQualifiedName())
                .WithDefaultDummyBaseConstructor(false, SyntaxKind.PublicKeyword);

            r4ControllerClass = AddMethodOverrides(r4ControllerClass, controllerSymbol);
            return(r4ControllerClass);
        }
Beispiel #4
0
 private ConstructorDeclarationSyntax CreateIActionResultConstructor(string className)
 => ConstructorDeclaration(className)
 .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")))));
Beispiel #5
0
 public IEnumerable <ClassDeclarationSyntax> CreateAreaClasses(ILookup <string, ControllerDefinition> areaControllers)
 {
     foreach (var area in areaControllers.Where(a => !string.IsNullOrEmpty(a.Key)).OrderBy(a => a.Key))
     {
         var areaClass = ClassDeclaration(area.Key + "AreaClass")
                         .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword)
                         .WithGeneratedNonUserCodeAttributes()
                         .WithStringField("Name", area.Key, false, SyntaxKind.PublicKeyword, SyntaxKind.ReadOnlyKeyword)
                         .AddMembers(area
                                     .OrderBy(c => c.Namespace == null).ThenBy(c => c.Name)
                                     .Select(c => SyntaxNodeHelpers.CreateFieldWithDefaultInitializer(
                                                 c.Name,
                                                 c.FullyQualifiedGeneratedName,
                                                 c.FullyQualifiedR4ClassName ?? c.FullyQualifiedGeneratedName,
                                                 SyntaxKind.PublicKeyword,
                                                 SyntaxKind.ReadOnlyKeyword))
                                     .ToArray <MemberDeclarationSyntax>());
         yield return(areaClass);
     }
 }
Beispiel #6
0
        private ClassDeclarationSyntax AddParameterlessMethods(ClassDeclarationSyntax node, ITypeSymbol mvcSymbol)
        {
            var methods = mvcSymbol.GetPublicNonGeneratedMethods()
                          .GroupBy(m => m.Name)
                          .Where(g => !g.Any(m => m.Parameters.Length == 0))
                          .Select(g => MethodDeclaration(IdentifierName("IActionResult"), Identifier(g.Key))
                                  .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.VirtualKeyword)
                                  .WithAttributes(SyntaxNodeHelpers.CreateNonActionAttribute())
                                  .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                                  .WithBody(
                                      Block(
                                          // return new R4Mvc_Microsoft_AspNetCore_Mvc_ActionResult(Area, Name, ActionNames.{Action});
                                          ReturnStatement(
                                              ObjectCreationExpression(IdentifierName(Constants.ActionResultClass))
                                              .WithArgumentList(
                                                  IdentifierName("Area"),
                                                  IdentifierName("Name"),
                                                  SyntaxNodeHelpers.MemberAccess("ActionNames", g.Key))))));

            return(node.AddMembers(methods.ToArray()));
        }
Beispiel #7
0
        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);
            }
        }
Beispiel #9
0
        public async Task Run(string projectPath)
        {
            var projectRoot = Path.GetDirectoryName(projectPath);

            // Load the project and check for compilation errors
            var workspace = MSBuildWorkspace.Create();

            DumpMsBuildAssemblies("clean workspace");

            var project = await workspace.OpenProjectAsync(projectPath);

            DumpMsBuildAssemblies("project loaded");
            if (workspace.Diagnostics.Count > 0)
            {
                var foundErrors = false;
                foreach (var diag in workspace.Diagnostics)
                {
                    Console.Error.WriteLine($"  {diag.Kind}: {diag.Message}");
                    if (diag.Kind == Microsoft.CodeAnalysis.WorkspaceDiagnosticKind.Failure)
                    {
                        foundErrors = true;
                    }
                }
                if (foundErrors)
                {
                    return;
                }
            }

            // Prep the project Compilation object, and process the Controller public methods list
            var compilation = await project.GetCompilationAsync() as CSharpCompilation;

            SyntaxNodeHelpers.PopulateControllerClassMethodNames(compilation);

            // Analyse the controllers in the project (updating them to be partial), as well as locate all the view files
            var controllers  = _controllerRewriter.RewriteControllers(compilation);
            var allViewFiles = _viewLocators.SelectMany(x => x.Find(projectRoot));

            // Assign view files to controllers
            foreach (var views in allViewFiles.GroupBy(v => new { v.AreaName, v.ControllerName }))
            {
                var controller = controllers
                                 .Where(c => string.Equals(c.Name, views.Key.ControllerName, StringComparison.OrdinalIgnoreCase))
                                 .Where(c => string.Equals(c.Area, views.Key.AreaName, StringComparison.OrdinalIgnoreCase))
                                 .FirstOrDefault();
                if (controller == null)
                {
                    controllers.Add(controller = new ControllerDefinition
                    {
                        Area = views.Key.AreaName,
                        Name = views.Key.ControllerName,
                    });
                }
                foreach (var view in views)
                {
                    controller.Views.Add(view);
                }
            }

            // Generate mappings for area names, to avoid clashes with controller names
            var areaMap = GenerateAreaMap(controllers);

            foreach (var controller in controllers.Where(a => !string.IsNullOrEmpty(a.Area)))
            {
                controller.AreaKey = areaMap[controller.Area];
            }

            // Generate the R4Mvc.generated.cs file
            _generatorService.Generate(projectRoot, controllers);
        }
Beispiel #10
0
        private ClassDeclarationSyntax AddMethodOverrides(ClassDeclarationSyntax node, ITypeSymbol mvcSymbol)
        {
            const string overrideMethodSuffix = "Override";
            var          methods = mvcSymbol.GetPublicNonGeneratedMethods()
                                   .SelectMany(m =>
            {
                var statements = new List <StatementSyntax>
                {
                    // var callInfo = new R4Mvc_Microsoft_AspNetCore_Mvc_ActionResult(Area, Name, ActionNames.{Action});
                    LocalDeclarationStatement(
                        SyntaxNodeHelpers.VariableDeclaration("callInfo",
                                                              ObjectCreationExpression(IdentifierName(Constants.ActionResultClass))
                                                              .WithArgumentList(
                                                                  IdentifierName("Area"),
                                                                  IdentifierName("Name"),
                                                                  SyntaxNodeHelpers.MemberAccess("ActionNames", m.Name)))),
                };
                foreach (var param in m.Parameters)
                {
                    statements.Add(
                        ExpressionStatement(
                            InvocationExpression(
                                SyntaxNodeHelpers.MemberAccess("ModelUnbinderHelpers", "AddRouteValues"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("callInfo", "RouteValueDictionary"),
                                LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(param.Name)),
                                IdentifierName(param.Name))));
                }
                statements.Add(
                    // {Action}Override(callInfo, {parameters});
                    ExpressionStatement(
                        InvocationExpression(IdentifierName(m.Name + overrideMethodSuffix))
                        .WithArgumentList(
                            new[] { IdentifierName("callInfo") }
                            .Concat(m.Parameters.Select(p => IdentifierName(p.Name)))
                            .ToArray())));
                statements.Add(
                    // return callInfo;
                    m.ReturnType.ToString().Contains("Task<")
                            ? ReturnStatement(
                        InvocationExpression(
                            SyntaxNodeHelpers.MemberAccess("Task", "FromResult"))
                        .WithArgumentList(
                            BinaryExpression(
                                SyntaxKind.AsExpression,
                                IdentifierName("callInfo"),
                                IdentifierName(m.ReturnType.ToString().Substring(m.ReturnType.ToString().IndexOf('<') + 1).TrimEnd('>')))))
                            : ReturnStatement(IdentifierName("callInfo")));
                return(new[]
                {
                    MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(m.Name + overrideMethodSuffix))
                    .WithModifiers(SyntaxKind.PartialKeyword)
                    .WithAttributes(SyntaxNodeHelpers.CreateNonActionAttribute())
                    .AddParameterListParameters(
                        Parameter(Identifier("callInfo")).WithType(IdentifierName(Constants.ActionResultClass)))
                    .AddParameterListParameters(m.Parameters
                                                .Select(p => Parameter(Identifier(p.Name))
                                                        .WithType(IdentifierName(p.Type.ToString())))
                                                .ToArray())
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                    MethodDeclaration(IdentifierName(m.ReturnType.ToString()), Identifier(m.Name))
                    .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.OverrideKeyword)
                    .WithAttributes(SyntaxNodeHelpers.CreateNonActionAttribute())
                    .AddParameterListParameters(m.Parameters
                                                .Select(p => Parameter(Identifier(p.Name))
                                                        .WithType(IdentifierName(p.Type.ToString())))
                                                .ToArray())
                    .WithBody(
                        Block(statements.ToArray())),
                });
            });

            return(node.AddMembers(methods.ToArray()));
        }
Beispiel #11
0
        private ClassDeclarationSyntax AddRedirectMethods(ClassDeclarationSyntax node)
        {
            var methods = new[]
            {
                MethodDeclaration(IdentifierName("RedirectToRouteResult"), Identifier("RedirectToAction"))
                .WithModifiers(SyntaxKind.ProtectedKeyword)
                .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                .AddParameterListParameters(
                    Parameter(Identifier("result")).WithType(IdentifierName("IActionResult")))
                .WithBody(
                    Block(
                        // var callInfo = result.GetR4MvcResult();
                        LocalDeclarationStatement(
                            SyntaxNodeHelpers.VariableDeclaration("callInfo",
                                                                  InvocationExpression(SyntaxNodeHelpers.MemberAccess("result", "GetR4MvcResult")))),
                        // return RedirectToRoute(callInfo.RouteValueDictionary);
                        ReturnStatement(
                            InvocationExpression(IdentifierName("RedirectToRoute"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("callInfo", "RouteValueDictionary"))))),
                MethodDeclaration(IdentifierName("RedirectToRouteResult"), Identifier("RedirectToAction"))
                .WithModifiers(SyntaxKind.ProtectedKeyword)
                .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                .AddParameterListParameters(
                    Parameter(Identifier("taskResult")).WithGenericType("Task", "IActionResult"))
                .WithBody(
                    Block(
                        // return RedirectToAction(taskResult.Result);
                        ReturnStatement(
                            InvocationExpression(IdentifierName("RedirectToAction"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("taskResult", "Result"))))),
                MethodDeclaration(IdentifierName("RedirectToRouteResult"), Identifier("RedirectToActionPermanent"))
                .WithModifiers(SyntaxKind.ProtectedKeyword)
                .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                .AddParameterListParameters(
                    Parameter(Identifier("result")).WithType(IdentifierName("IActionResult")))
                .WithBody(
                    Block(
                        // var callInfo = result.GetR4MvcResult();
                        LocalDeclarationStatement(
                            SyntaxNodeHelpers.VariableDeclaration("callInfo",
                                                                  InvocationExpression(SyntaxNodeHelpers.MemberAccess("result", "GetR4MvcResult")))),
                        // return RedirectToRoutePermanent(callInfo.RouteValueDictionary);
                        ReturnStatement(
                            InvocationExpression(IdentifierName("RedirectToRoutePermanent"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("callInfo", "RouteValueDictionary"))))),
                MethodDeclaration(IdentifierName("RedirectToRouteResult"), Identifier("RedirectToActionPermanent"))
                .WithModifiers(SyntaxKind.ProtectedKeyword)
                .WithAttributes(SyntaxNodeHelpers.CreateGeneratedCodeAttribute(), SyntaxNodeHelpers.CreateDebugNonUserCodeAttribute())
                .AddParameterListParameters(
                    Parameter(Identifier("taskResult")).WithGenericType("Task", "IActionResult"))
                .WithBody(
                    Block(
                        // return RedirectToActionPermanent(taskResult.Result);
                        ReturnStatement(
                            InvocationExpression(IdentifierName("RedirectToActionPermanent"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("taskResult", "Result"))))),
            };

            return(node.AddMembers(methods));
        }
Beispiel #12
0
        public void Generate(string projectRoot, IList <ControllerDefinition> controllers)
        {
            var areaControllers = controllers.ToLookup(c => c.Area);

            // Processing controllers, generating partial and derived controller classes for R4Mvc
            var generatedControllers = new List <NamespaceDeclarationSyntax>();

            foreach (var namespaceGroup in controllers.Where(c => c.Namespace != null).GroupBy(c => c.Namespace).OrderBy(c => c.Key))
            {
                var namespaceNode = NamespaceDeclaration(ParseName(namespaceGroup.Key));
                foreach (var controller in namespaceGroup.OrderBy(c => c.Name))
                {
                    namespaceNode = namespaceNode.AddMembers(
                        _controllerGenerator.GeneratePartialController(controller),
                        _controllerGenerator.GenerateR4Controller(controller));

                    // If SplitIntoMultipleFiles is set, store the generated classes alongside the controller files.
                    if (_settings.SplitIntoMultipleFiles)
                    {
                        var controllerFile = NewCompilationUnit()
                                             .AddMembers(namespaceNode);
                        CompleteAndWriteFile(controllerFile, controller.GetFilePath().TrimEnd(".cs") + ".generated.cs");
                        namespaceNode = NamespaceDeclaration(ParseName(namespaceGroup.Key));
                    }
                }

                // If SplitIntoMultipleFiles is NOT set, bundle them all in R4Mvc
                if (!_settings.SplitIntoMultipleFiles)
                {
                    generatedControllers.Add(namespaceNode);
                }
            }

            // R4MVC namespace used for the areas and Dummy class
            var r4Namespace = NamespaceDeclaration(ParseName(_settings.R4MvcNamespace))
                              // add the dummy class using in the derived controller partial class
                              .WithDummyClass()
                              .AddMembers(CreateViewOnlyControllerClasses(controllers).ToArray <MemberDeclarationSyntax>())
                              .AddMembers(CreateAreaClasses(areaControllers).ToArray <MemberDeclarationSyntax>());

            // create static MVC class and add the area and controller fields
            var mvcStaticClass = ClassDeclaration(_settings.HelpersPrefix)
                                 .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword)
                                 .WithGeneratedNonUserCodeAttributes();

            foreach (var area in areaControllers.Where(a => !string.IsNullOrEmpty(a.Key)).OrderBy(a => a.Key))
            {
                mvcStaticClass = mvcStaticClass.WithStaticFieldBackedProperty(area.First().AreaKey, $"{_settings.R4MvcNamespace}.{area.Key}AreaClass", SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword);
            }
            foreach (var controller in areaControllers[string.Empty].OrderBy(c => c.Namespace == null).ThenBy(c => c.Name))
            {
                mvcStaticClass = mvcStaticClass.AddMembers(
                    SyntaxNodeHelpers.CreateFieldWithDefaultInitializer(
                        controller.Name,
                        controller.FullyQualifiedGeneratedName,
                        controller.FullyQualifiedR4ClassName ?? controller.FullyQualifiedGeneratedName,
                        SyntaxKind.PublicKeyword,
                        SyntaxKind.StaticKeyword,
                        SyntaxKind.ReadOnlyKeyword));
            }

            // Generate a list of all static files from the wwwroot path
            var staticFileNode = _staticFileGenerator.GenerateStaticFiles(projectRoot);

            var r4mvcNode = NewCompilationUnit()
                            .AddMembers(
                mvcStaticClass,
                r4Namespace,
                staticFileNode,
                ActionResultClass(),
                JsonResultClass(),
                ContentResultClass(),
                RedirectResultClass(),
                RedirectToActionResultClass(),
                RedirectToRouteResultClass())
                            .AddMembers(generatedControllers.ToArray <MemberDeclarationSyntax>());

            CompleteAndWriteFile(r4mvcNode, Path.Combine(projectRoot, R4MvcGeneratorService.R4MvcFileName));
        }
Beispiel #13
0
        public ClassDeclarationSyntax GeneratePartialController(ControllerDefinition controller)
        {
            // build controller partial class node
            // add a default constructor if there are some but none are zero length
            var genControllerClass = ClassDeclaration(controller.Symbol.Name)
                                     .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.PartialKeyword);
            var controllerTypeParams = controller.Symbol.TypeParameters.Select(tp => TypeParameter(tp.Name)).ToArray();

            if (controllerTypeParams.Length > 0)
            {
                genControllerClass = genControllerClass.AddTypeParameterListParameters(controllerTypeParams);
            }

            var gotCustomConstructors = controller.Symbol.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, controller.Symbol);
            var actionsExpression = controller.AreaKey != null
                ? SyntaxNodeHelpers.MemberAccess(_settings.HelpersPrefix + "." + controller.AreaKey, controller.Name)
                : SyntaxNodeHelpers.MemberAccess(_settings.HelpersPrefix, controller.Name);

            genControllerClass =
                genControllerClass
                .WithProperty("Actions", controller.Symbol.Name, actionsExpression, SyntaxKind.PublicKeyword)
                .WithStringField(
                    "Area",
                    controller.Area,
                    true,
                    SyntaxKind.PublicKeyword,
                    SyntaxKind.ReadOnlyKeyword)
                .WithStringField(
                    "Name",
                    controller.Name,
                    true,
                    SyntaxKind.PublicKeyword,
                    SyntaxKind.ReadOnlyKeyword)
                .WithStringField(
                    "NameConst",
                    controller.Name,
                    true,
                    SyntaxKind.PublicKeyword,
                    SyntaxKind.ConstKeyword)
                .WithStaticFieldBackedProperty("ActionNames", "ActionNamesClass", SyntaxKind.PublicKeyword)
                .WithActionNameClass(controller.Symbol)
                .WithActionConstantsClass(controller.Symbol)
                .WithViewsClass(controller.Name, controller.Area, controller.Views);

            return(genControllerClass);
        }
Beispiel #14
0
        private ClassDeclarationSyntax AddMethodOverrides(ClassDeclarationSyntax node, ITypeSymbol mvcSymbol)
        {
            const string overrideMethodSuffix = "Override";
            var          methods = mvcSymbol.GetPublicNonGeneratedMethods()
                                   .SelectMany(m =>
            {
                var callInfoType = Constants.ActionResultClass;
                if (m.ReturnType.InheritsFrom <JsonResult>())
                {
                    callInfoType = Constants.JsonResultClass;
                }
                else if (m.ReturnType.InheritsFrom <ContentResult>())
                {
                    callInfoType = Constants.ContentResultClass;
                }
                else if (m.ReturnType.InheritsFrom <RedirectResult>())
                {
                    callInfoType = Constants.RedirectResultClass;
                }
                else if (m.ReturnType.InheritsFrom <RedirectToActionResult>())
                {
                    callInfoType = Constants.RedirectToActionResultClass;
                }
                else if (m.ReturnType.InheritsFrom <RedirectToRouteResult>())
                {
                    callInfoType = Constants.RedirectToRouteResultClass;
                }

                var statements = new List <StatementSyntax>
                {
                    // var callInfo = new R4Mvc_Microsoft_AspNetCore_Mvc_ActionResult(Area, Name, ActionNames.{Action});
                    LocalDeclarationStatement(
                        SyntaxNodeHelpers.VariableDeclaration("callInfo",
                                                              ObjectCreationExpression(IdentifierName(callInfoType))
                                                              .WithArgumentList(
                                                                  IdentifierName("Area"),
                                                                  IdentifierName("Name"),
                                                                  SyntaxNodeHelpers.MemberAccess("ActionNames", m.Name)))),
                };
                foreach (var param in m.Parameters)
                {
                    statements.Add(
                        ExpressionStatement(
                            InvocationExpression(
                                SyntaxNodeHelpers.MemberAccess("ModelUnbinderHelpers", "AddRouteValues"))
                            .WithArgumentList(
                                SyntaxNodeHelpers.MemberAccess("callInfo", "RouteValueDictionary"),
                                LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(param.Name)),
                                IdentifierName(param.Name))));
                }
                statements.Add(
                    // {Action}Override(callInfo, {parameters});
                    ExpressionStatement(
                        InvocationExpression(IdentifierName(m.Name + overrideMethodSuffix))
                        .WithArgumentList(
                            new[] { IdentifierName("callInfo") }
                            .Concat(m.Parameters.Select(p => IdentifierName(p.Name)))
                            .ToArray())));
                var returnType = m.ReturnType as INamedTypeSymbol;
                statements.Add(
                    // return callInfo;
                    returnType.InheritsFrom <Task>() == true
                            ? ReturnStatement(
                        InvocationExpression(
                            SyntaxNodeHelpers.MemberAccess(typeof(Task).FullName, "FromResult"))
                        .WithArgumentList(
                            returnType.TypeArguments.Length > 0
                                            ? (ExpressionSyntax)BinaryExpression(
                                SyntaxKind.AsExpression,
                                IdentifierName("callInfo"),
                                IdentifierName(returnType.TypeArguments[0].ToString()))
                                            : IdentifierName("callInfo")
                            ))
                            : ReturnStatement(IdentifierName("callInfo")));
                return(new[]
                {
                    MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(m.Name + overrideMethodSuffix))
                    .WithModifiers(SyntaxKind.PartialKeyword)
                    .WithNonActionAttribute()
                    .AddParameterListParameters(
                        Parameter(Identifier("callInfo")).WithType(IdentifierName(callInfoType)))
                    .AddParameterListParameters(m.Parameters
                                                .Select(p => Parameter(Identifier(p.Name))
                                                        .WithType(IdentifierName(p.Type.ToString())))
                                                .ToArray())
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
                    MethodDeclaration(IdentifierName(m.ReturnType.ToString()), Identifier(m.Name))
                    .WithModifiers(SyntaxKind.PublicKeyword, SyntaxKind.OverrideKeyword)
                    .WithNonActionAttribute()
                    .AddParameterListParameters(m.Parameters
                                                .Select(p => Parameter(Identifier(p.Name))
                                                        .WithType(IdentifierName(p.Type.ToString())))
                                                .ToArray())
                    .WithBody(
                        Block(statements.ToArray())),
                });
            });

            return(node.AddMembers(methods.ToArray()));
        }