From b808c48fafe49f0dafa8d5d21285b528070004ba Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 23 Jun 2014 17:12:28 -0700 Subject: [PATCH] Add Model specific code generation. By adding model specific code generation we are able to map the generic "base type" code to the cshtml file. This also involved utilizing the CreateClassDeclaration method in the Microsoft.AspNet.Razor library. Lastly Added a MvcCSharpChunkVisitor to add one more abstraction layer between the Microsoft.AspNet.Razor code and the Mvc code; this makes it so Mvc follows the same class structure as the base Razor parser. #568 --- .../Microsoft.AspNet.Mvc.Razor.Host.kproj | 4 ++ .../ModelChunk.cs | 21 ++++++++++ .../ModelChunkVisitor.cs | 22 ++++++++++ .../ModelCodeGenerator.cs | 41 +++++++++++++++++++ .../MvcCSharpChunkVisitor.cs | 33 +++++++++++++++ .../MvcCSharpCodeBuilder.cs | 25 +++++++++++ .../MvcCSharpCodeVistor.cs | 21 +++------- .../MvcRazorCodeParser.cs | 9 +--- 8 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunk.cs create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunkVisitor.cs create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/ModelCodeGenerator.cs create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpChunkVisitor.cs diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Microsoft.AspNet.Mvc.Razor.Host.kproj b/src/Microsoft.AspNet.Mvc.Razor.Host/Microsoft.AspNet.Mvc.Razor.Host.kproj index 577f5a3481..227e4f8f8a 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/Microsoft.AspNet.Mvc.Razor.Host.kproj +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Microsoft.AspNet.Mvc.Razor.Host.kproj @@ -25,6 +25,10 @@ + + + + diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunk.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunk.cs new file mode 100644 index 0000000000..0b034fd66a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunk.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNet.Razor.Generator.Compiler; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class ModelChunk : Chunk + { + /// + /// Represents the chunk for an @model statement. + /// + /// The base type of the view. + /// The type of the view's Model. + public ModelChunk(string baseType, string modelType) + { + BaseType = baseType; + ModelType = modelType; + } + + public string BaseType { get; private set; } + public string ModelType { get; private set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunkVisitor.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunkVisitor.cs new file mode 100644 index 0000000000..8c38523769 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelChunkVisitor.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class ModelChunkVisitor : MvcCSharpCodeVisitor + { + public ModelChunkVisitor([NotNull] CSharpCodeWriter writer, + [NotNull] CodeGeneratorContext context) + : base(writer, context) + { } + + protected override void Visit(ModelChunk chunk) + { + var csharpVisitor = new CSharpCodeVisitor(Writer, Context); + + Writer.Write(chunk.BaseType).Write("<"); + csharpVisitor.CreateExpressionCodeMapping(chunk.ModelType, chunk); + Writer.Write(">"); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/ModelCodeGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelCodeGenerator.cs new file mode 100644 index 0000000000..632678dcba --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/ModelCodeGenerator.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.AspNet.Mvc.Razor; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Generator +{ + public class ModelCodeGenerator : SpanCodeGenerator + { + public ModelCodeGenerator(string baseType, string modelType) + { + BaseType = baseType; + ModelType = modelType; + } + + public string BaseType { get; private set; } + public string ModelType { get; private set; } + + public override void GenerateCode(Span target, CodeGeneratorContext context) + { + var modelChunk = new ModelChunk(BaseType, ModelType); + context.CodeTreeBuilder.AddChunk(modelChunk, target, topLevel: true); + } + + public override string ToString() + { + return "Full Model Type: " + BaseType + "<" + ModelType + ">"; + } + + public override bool Equals(object obj) + { + var other = obj as ModelCodeGenerator; + return other != null && + string.Equals(ModelType, other.ModelType, StringComparison.Ordinal); + } + + public override int GetHashCode() + { + return ModelType.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpChunkVisitor.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpChunkVisitor.cs new file mode 100644 index 0000000000..887466eed5 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpChunkVisitor.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public abstract class MvcCSharpChunkVisitor : CodeVisitor + { + public MvcCSharpChunkVisitor([NotNull] CSharpCodeWriter writer, + [NotNull] CodeGeneratorContext context) + : base(writer, context) + { } + + public override void Accept(Chunk chunk) + { + if (chunk is InjectChunk) + { + Visit((InjectChunk)chunk); + } + else if (chunk is ModelChunk) + { + Visit((ModelChunk)chunk); + } + else + { + base.Accept(chunk); + } + } + + protected abstract void Visit(InjectChunk chunk); + protected abstract void Visit(ModelChunk chunk); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeBuilder.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeBuilder.cs index a3a9ac5c63..c7a510feda 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeBuilder.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeBuilder.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Globalization; using System.Linq; using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; namespace Microsoft.AspNet.Mvc.Razor @@ -15,6 +17,29 @@ namespace Microsoft.AspNet.Mvc.Razor { } + protected override CSharpCodeWritingScope BuildClassDeclaration(CSharpCodeWriter writer) + { + var chunks = Context.CodeTreeBuilder.CodeTree.Chunks; + + // If there were any model chunks then we need to modify the class declaration signature. + if (chunks.OfType().Any()) + { + writer.Write(string.Format(CultureInfo.CurrentCulture, "public class {0} : ", Context.ClassName)); + + var modelVisitor = new ModelChunkVisitor(writer, Context); + // This generates the base class signature + modelVisitor.Accept(chunks); + + writer.WriteLine(); + + return new CSharpCodeWritingScope(writer); + } + else + { + return base.BuildClassDeclaration(writer); + } + } + protected override void BuildConstructor([NotNull] CSharpCodeWriter writer) { writer.WriteLineHiddenDirective(); diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeVistor.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeVistor.cs index 89a843d307..334873d574 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeVistor.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcCSharpCodeVistor.cs @@ -2,31 +2,22 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNet.Razor.Generator; -using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; namespace Microsoft.AspNet.Mvc.Razor { - public abstract class MvcCSharpCodeVisitor : CodeVisitor + public abstract class MvcCSharpCodeVisitor : MvcCSharpChunkVisitor { - public MvcCSharpCodeVisitor([NotNull] CSharpCodeWriter writer, + public MvcCSharpCodeVisitor([NotNull] CSharpCodeWriter writer, [NotNull] CodeGeneratorContext context) : base(writer, context) + { } + + protected override void Visit(InjectChunk chunk) { } - - public override void Accept(Chunk chunk) + protected override void Visit(ModelChunk chunk) { - if (chunk is InjectChunk) - { - Visit((InjectChunk)chunk); - } - else - { - base.Accept(chunk); - } } - - protected abstract void Visit(InjectChunk chunk); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs index 6b19279dac..6601e4920c 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorCodeParser.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Globalization; using Microsoft.AspNet.Mvc.Razor.Host; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser; @@ -13,7 +12,6 @@ namespace Microsoft.AspNet.Mvc.Razor { public class MvcRazorCodeParser : CSharpCodeParser { - private const string GenericTypeFormat = "{0}<{1}>"; private const string ModelKeyword = "model"; private const string InjectKeyword = "inject"; private readonly string _baseType; @@ -138,12 +136,7 @@ namespace Microsoft.AspNet.Mvc.Razor private SpanCodeGenerator CreateModelCodeGenerator(string model) { - // In the event we have an empty model, the name we generate does not matter since it's a parser error. - // We'll use the non-generic version of the base type. - var baseType = string.IsNullOrEmpty(model) ? - _baseType : - string.Format(CultureInfo.InvariantCulture, GenericTypeFormat, _baseType, model); - return new SetBaseTypeCodeGenerator(baseType); + return new ModelCodeGenerator(_baseType, model); } } }