Fix #1311 add a base class for features

This commit is contained in:
Ryan Nowak 2017-05-12 20:49:19 -07:00
parent 72fe4cc952
commit 2167e4151e
27 changed files with 180 additions and 120 deletions

View File

@ -1,19 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultDirectiveSyntaxTreePass : IRazorSyntaxTreePass
internal class DefaultDirectiveSyntaxTreePass : RazorEngineFeatureBase, IRazorSyntaxTreePass
{
public RazorEngine Engine { get; set; }
public int Order => 75;
public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree)
{
if (codeDocument == null)
{
throw new ArgumentNullException(nameof(codeDocument));
}
if (syntaxTree == null)
{
throw new ArgumentNullException(nameof(syntaxTree));
}
var errorSink = new ErrorSink();
var sectionVerifier = new NestedSectionVerifier();
sectionVerifier.Verify(syntaxTree, errorSink);

View File

@ -7,10 +7,8 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultDocumentClassifierPassFeature : IRazorEngineFeature
internal class DefaultDocumentClassifierPassFeature : RazorEngineFeatureBase
{
public RazorEngine Engine { get; set; }
public IList<Action<RazorCodeDocument, ClassDeclarationIRNode>> ConfigureClass { get; } =
new List<Action<RazorCodeDocument, ClassDeclarationIRNode>>();

View File

@ -18,10 +18,10 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var irDocument = codeDocument.GetIRDocument();
ThrowForMissingDependency(irDocument);
ThrowForMissingDocumentDependency(irDocument);
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDependency(syntaxTree);
ThrowForMissingDocumentDependency(syntaxTree);
var target = irDocument.Target;
if (target == null)

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var irDocument = codeDocument.GetIRDocument();
ThrowForMissingDependency(irDocument);
ThrowForMissingDocumentDependency(irDocument);
foreach (var pass in Passes)
{

View File

@ -1,20 +1,24 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorDirectiveFeature : IRazorDirectiveFeature, IRazorParserOptionsFeature
internal class DefaultRazorDirectiveFeature : RazorEngineFeatureBase, IRazorDirectiveFeature, IRazorParserOptionsFeature
{
public ICollection<DirectiveDescriptor> Directives { get; } = new List<DirectiveDescriptor>();
public RazorEngine Engine { get; set; }
public int Order => 100;
void IRazorParserOptionsFeature.Configure(RazorParserOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.Directives.Clear();
foreach (var directive in Directives)

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var irDocument = codeDocument.GetIRDocument();
ThrowForMissingDependency(irDocument);
ThrowForMissingDocumentDependency(irDocument);
foreach (var pass in Passes)
{

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDependency(syntaxTree);
ThrowForMissingDocumentDependency(syntaxTree);
// This might not have been set if there are no tag helpers.
var tagHelperContext = codeDocument.GetTagHelperContext();

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var irDocument = codeDocument.GetIRDocument();
ThrowForMissingDependency(irDocument);
ThrowForMissingDocumentDependency(irDocument);
foreach (var pass in Passes)
{

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDependency(syntaxTree);
ThrowForMissingDocumentDependency(syntaxTree);
foreach (var pass in Passes)
{

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDependency(syntaxTree);
ThrowForMissingDocumentDependency(syntaxTree);
var resolver = Engine.Features.OfType<ITagHelperFeature>().FirstOrDefault()?.Resolver;
if (resolver == null)

View File

@ -6,10 +6,8 @@ using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultRazorTargetExtensionFeature : IRazorTargetExtensionFeature
internal class DefaultRazorTargetExtensionFeature : RazorEngineFeatureBase, IRazorTargetExtensionFeature
{
public RazorEngine Engine { get; set; }
public ICollection<ICodeTargetExtension> TargetExtensions { get; } = new List<ICodeTargetExtension>();
}
}

View File

@ -3,10 +3,8 @@
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DesignTimeParserOptionsFeature : IRazorParserOptionsFeature
internal class DesignTimeParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
{
public RazorEngine Engine { get; set; }
public int Order { get; set; }
public void Configure(RazorParserOptions options)

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language
protected ICodeTargetExtension[] TargetExtensions { get; private set; }
protected override void OnIntialized()
protected override void OnInitialized()
{
var feature = Engine.Features.OfType<IRazorTargetExtensionFeature>();

View File

@ -1,18 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class HtmlNodeOptimizationPass : IRazorSyntaxTreePass
internal class HtmlNodeOptimizationPass : RazorEngineFeatureBase, IRazorSyntaxTreePass
{
public RazorEngine Engine { get; set; }
public int Order => 100;
public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree)
{
if (codeDocument == null)
{
throw new ArgumentNullException(nameof(codeDocument));
}
if (syntaxTree == null)
{
throw new ArgumentNullException(nameof(syntaxTree));
}
var conditionalAttributeCollapser = new ConditionalAttributeCollapser();
var rewritten = conditionalAttributeCollapser.Rewrite(syntaxTree.Root);

View File

@ -402,6 +402,20 @@ namespace Microsoft.AspNetCore.Razor.Language
internal static string FormatDirectiveDescriptor_InvalidDirectiveName(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_InvalidDirectiveName"), p0);
/// <summary>
/// The feature must be initialized by setting the '{0}' property.
/// </summary>
internal static string FeatureMustBeInitialized
{
get => GetString("FeatureMustBeInitialized");
}
/// <summary>
/// The feature must be initialized by setting the '{0}' property.
/// </summary>
internal static string FormatFeatureMustBeInitialized(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("FeatureMustBeInitialized"), p0);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -0,0 +1,69 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class RazorEngineFeatureBase : IRazorEngineFeature
{
private RazorEngine _engine;
public RazorEngine Engine
{
get { return _engine; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_engine = value;
OnInitialized();
}
}
protected T GetRequiredFeature<T>() where T : IRazorEngineFeature
{
if (Engine == null)
{
throw new InvalidOperationException(Resources.FormatFeatureMustBeInitialized(nameof(Engine)));
}
var feature = Engine.Features.OfType<T>().FirstOrDefault();
ThrowForMissingEngineDependency<T>(feature);
return feature;
}
protected void ThrowForMissingDocumentDependency<TDocumentDependency>(TDocumentDependency value)
{
if (value == null)
{
throw new InvalidOperationException(
Resources.FormatFeatureDependencyMissing(
GetType().Name,
typeof(TDocumentDependency).Name,
typeof(RazorCodeDocument).Name));
}
}
protected void ThrowForMissingEngineDependency<TEngineDependency>(TEngineDependency value)
{
if (value == null)
{
throw new InvalidOperationException(
Resources.FormatFeatureDependencyMissing(
GetType().Name,
typeof(TEngineDependency).Name,
typeof(RazorEngine).Name));
}
}
protected virtual void OnInitialized()
{
}
}
}

View File

@ -2,10 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
internal abstract class RazorEnginePhaseBase : IRazorEnginePhase
public abstract class RazorEnginePhaseBase : IRazorEnginePhase
{
private RazorEngine _engine;
@ -39,14 +40,40 @@ namespace Microsoft.AspNetCore.Razor.Language
ExecuteCore(codeDocument);
}
protected void ThrowForMissingDependency<T>(T value)
protected T GetRequiredFeature<T>()
{
if (Engine == null)
{
throw new InvalidOperationException(Resources.FormatFeatureMustBeInitialized(nameof(Engine)));
}
var feature = Engine.Features.OfType<T>().FirstOrDefault();
ThrowForMissingEngineDependency<T>(feature);
return feature;
}
protected void ThrowForMissingDocumentDependency<TDocumentDependency>(TDocumentDependency value)
{
if (value == null)
{
throw new InvalidOperationException(Resources.FormatPhaseDependencyMissing(
GetType().Name,
typeof(T).Name,
typeof(RazorCodeDocument).Name));
throw new InvalidOperationException(
Resources.FormatPhaseDependencyMissing(
GetType().Name,
typeof(TDocumentDependency).Name,
typeof(RazorCodeDocument).Name));
}
}
protected void ThrowForMissingEngineDependency<TEngineDependency>(TEngineDependency value)
{
if (value == null)
{
throw new InvalidOperationException(
Resources.FormatPhaseDependencyMissing(
GetType().Name,
typeof(TEngineDependency).Name,
typeof(RazorEngine).Name));
}
}

View File

@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class RazorIRPassBase
public abstract class RazorIRPassBase : RazorEngineFeatureBase
{
/// <summary>
/// The default implementation of the <see cref="IRazorEngineFeature"/>s that run in a
@ -18,53 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Language
/// </remarks>
public static readonly int DefaultFeatureOrder = 1000;
private RazorEngine _engine;
public RazorEngine Engine
{
get { return _engine; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_engine = value;
OnIntialized();
}
}
public virtual int Order { get; }
protected void ThrowForMissingDocumentDependency<TDocumentDependency>(TDocumentDependency value)
{
if (value == null)
{
throw new InvalidOperationException(
Resources.FormatFeatureDependencyMissing(
GetType().Name,
typeof(TDocumentDependency).Name,
typeof(RazorEngine).Name));
}
}
protected void ThrowForMissingEngineDependency<TEngineDependency>(TEngineDependency value)
{
if (value == null)
{
throw new InvalidOperationException(
Resources.FormatFeatureDependencyMissing(
GetType().Name,
typeof(TEngineDependency).Name,
typeof(RazorCodeDocument).Name));
}
}
protected virtual void OnIntialized()
{
}
public void Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
{
if (codeDocument == null)

View File

@ -201,4 +201,7 @@
<data name="DirectiveDescriptor_InvalidDirectiveName" xml:space="preserve">
<value>Invalid directive name '{0}'. Directives must have a non-empty name that consists only of letters.</value>
</data>
<data name="FeatureMustBeInitialized" xml:space="preserve">
<value>The feature must be initialized by setting the '{0}' property.</value>
</data>
</root>

View File

@ -6,10 +6,8 @@ using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.CodeAnalysis.Razor
{
public class DefaultMetadataReferenceFeature : IMetadataReferenceFeature
public class DefaultMetadataReferenceFeature : RazorEngineFeatureBase, IMetadataReferenceFeature
{
public RazorEngine Engine { get; set; }
public IReadOnlyList<MetadataReference> References { get; set; }
}
}

View File

@ -2,37 +2,19 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.CodeAnalysis.Razor
{
public class DefaultTagHelperFeature : ITagHelperFeature
public class DefaultTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature
{
private RazorEngine _engine;
private IMetadataReferenceFeature _referenceFeature;
public RazorEngine Engine
{
get
{
return _engine;
}
set
{
_engine = value;
OnInitialized();
}
}
public ITagHelperDescriptorResolver Resolver { get; private set; }
private void OnInitialized()
protected override void OnInitialized()
{
_referenceFeature = Engine.Features.OfType<IMetadataReferenceFeature>().FirstOrDefault();
Resolver = new InnerResolver(_referenceFeature);
Resolver = new InnerResolver(GetRequiredFeature<IMetadataReferenceFeature>());
}
private class InnerResolver : ITagHelperDescriptorResolver

View File

@ -562,7 +562,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.IntegrationTests
.Build();
}
private static IRazorEngineFeature GetMetadataReferenceFeature()
private static IMetadataReferenceFeature GetMetadataReferenceFeature()
{
var currentAssembly = typeof(CodeGenerationIntegrationTest).GetTypeInfo().Assembly;
var dependencyContext = DependencyContext.Load(currentAssembly);

View File

@ -206,15 +206,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
}
}
private class TagHelperFeature : ITagHelperFeature
private class TagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature
{
public TagHelperFeature(TagHelperDescriptor[] tagHelpers)
{
Resolver = new TagHelperDescriptorResolver(tagHelpers);
}
public RazorEngine Engine { get; set; }
public ITagHelperDescriptorResolver Resolver { get; }
}

View File

@ -364,15 +364,13 @@ public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore.
}
}
private class TagHelperFeature : ITagHelperFeature
private class TagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature
{
public TagHelperFeature(TagHelperDescriptor[] tagHelpers)
{
Resolver = new TagHelperDescriptorResolver(tagHelpers);
}
public RazorEngine Engine { get; set; }
public ITagHelperDescriptorResolver Resolver { get; }
}

View File

@ -74,10 +74,8 @@ namespace Microsoft.AspNetCore.Razor.Language
t => { Assert.Same(t.Source, imports[1]); Assert.Equal("test", Assert.Single(t.Options.Directives).Name); });
}
private class MyParserOptionsFeature : IRazorParserOptionsFeature
private class MyParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature
{
public RazorEngine Engine { get; set; }
public int Order { get; }
public void Configure(RazorParserOptions options)

View File

@ -52,11 +52,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var root = context.Builder.Build();
var diagnostics = context.ErrorSink.Errors?.Select(error => RazorDiagnostic.Create(error));
var tree = RazorSyntaxTree.Create(root, source, diagnostics, options);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();
tree = defaultDirectivePass.Execute(codeDocument: null, syntaxTree: tree);
var codeDocument = RazorCodeDocument.Create(source);
return tree;
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
codeDocument.SetSyntaxTree(syntaxTree);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();
syntaxTree = defaultDirectivePass.Execute(codeDocument, syntaxTree);
return syntaxTree;
}
internal virtual RazorSyntaxTree ParseHtmlBlock(string document, bool designTime = false)

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
{
public class TestTagHelperFeature : ITagHelperFeature
public class TestTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature
{
public TestTagHelperFeature()
{
@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Razor.Language
Resolver = new TestTagHelperDescriptorResolver(tagHelpers);
}
public RazorEngine Engine { get; set; }
public List<TagHelperDescriptor> TagHelpers => ((TestTagHelperDescriptorResolver)Resolver).TagHelpers;
public ITagHelperDescriptorResolver Resolver { get; }