155 lines
5.8 KiB
C#
155 lines
5.8 KiB
C#
// 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.Collections.Generic;
|
|
using System.Linq;
|
|
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|
|
|
namespace Microsoft.AspNetCore.Razor.Language
|
|
{
|
|
public abstract class DocumentClassifierPassBase : IntermediateNodePassBase, IRazorDocumentClassifierPass
|
|
{
|
|
private static readonly ICodeTargetExtension[] EmptyExtensionArray = new ICodeTargetExtension[0];
|
|
|
|
protected abstract string DocumentKind { get; }
|
|
|
|
protected ICodeTargetExtension[] TargetExtensions { get; private set; }
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
var feature = Engine.Features.OfType<IRazorTargetExtensionFeature>();
|
|
TargetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? EmptyExtensionArray;
|
|
}
|
|
|
|
protected sealed override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
|
{
|
|
if (documentNode.DocumentKind != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!IsMatch(codeDocument, documentNode))
|
|
{
|
|
return;
|
|
}
|
|
|
|
documentNode.DocumentKind = DocumentKind;
|
|
documentNode.Target = CreateTarget(codeDocument, documentNode.Options);
|
|
|
|
Rewrite(codeDocument, documentNode);
|
|
}
|
|
|
|
private void Rewrite(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
|
{
|
|
// Rewrite the document from a flat structure to use a sensible default structure,
|
|
// a namespace and class declaration with a single 'razor' method.
|
|
var children = new List<IntermediateNode>(documentNode.Children);
|
|
documentNode.Children.Clear();
|
|
|
|
var @namespace = new NamespaceDeclarationIntermediateNode();
|
|
@namespace.Annotations[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace;
|
|
|
|
var @class = new ClassDeclarationIntermediateNode();
|
|
@class.Annotations[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass;
|
|
|
|
var method = new MethodDeclarationIntermediateNode();
|
|
method.Annotations[CommonAnnotations.PrimaryMethod] = CommonAnnotations.PrimaryMethod;
|
|
|
|
var documentBuilder = IntermediateNodeBuilder.Create(documentNode);
|
|
|
|
var namespaceBuilder = IntermediateNodeBuilder.Create(documentBuilder.Current);
|
|
namespaceBuilder.Push(@namespace);
|
|
|
|
var classBuilder = IntermediateNodeBuilder.Create(namespaceBuilder.Current);
|
|
classBuilder.Push(@class);
|
|
|
|
var methodBuilder = IntermediateNodeBuilder.Create(classBuilder.Current);
|
|
methodBuilder.Push(method);
|
|
|
|
var visitor = new Visitor(documentBuilder, namespaceBuilder, classBuilder, methodBuilder);
|
|
|
|
for (var i = 0; i < children.Count; i++)
|
|
{
|
|
visitor.Visit(children[i]);
|
|
}
|
|
|
|
// Note that this is called at the *end* of rewriting so that user code can see the tree
|
|
// and look at its content to make a decision.
|
|
OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
|
|
}
|
|
|
|
protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode);
|
|
|
|
private CodeTarget CreateTarget(RazorCodeDocument codeDocument, RazorCodeGenerationOptions options)
|
|
{
|
|
return CodeTarget.CreateDefault(codeDocument, options, (builder) =>
|
|
{
|
|
for (var i = 0; i < TargetExtensions.Length; i++)
|
|
{
|
|
builder.TargetExtensions.Add(TargetExtensions[i]);
|
|
}
|
|
|
|
ConfigureTarget(builder);
|
|
});
|
|
}
|
|
|
|
protected virtual void ConfigureTarget(ICodeTargetBuilder builder)
|
|
{
|
|
// Intentionally empty.
|
|
}
|
|
|
|
protected virtual void OnDocumentStructureCreated(
|
|
RazorCodeDocument codeDocument,
|
|
NamespaceDeclarationIntermediateNode @namespace,
|
|
ClassDeclarationIntermediateNode @class,
|
|
MethodDeclarationIntermediateNode @method)
|
|
{
|
|
// Intentionally empty.
|
|
}
|
|
|
|
private class Visitor : IntermediateNodeVisitor
|
|
{
|
|
private readonly IntermediateNodeBuilder _document;
|
|
private readonly IntermediateNodeBuilder _namespace;
|
|
private readonly IntermediateNodeBuilder _class;
|
|
private readonly IntermediateNodeBuilder _method;
|
|
|
|
public Visitor(IntermediateNodeBuilder document, IntermediateNodeBuilder @namespace, IntermediateNodeBuilder @class, IntermediateNodeBuilder method)
|
|
{
|
|
_document = document;
|
|
_namespace = @namespace;
|
|
_class = @class;
|
|
_method = method;
|
|
}
|
|
|
|
public override void VisitUsingDirective(UsingDirectiveIntermediateNode node)
|
|
{
|
|
var children = _namespace.Current.Children;
|
|
var i = children.Count - 1;
|
|
for (; i >= 0; i--)
|
|
{
|
|
var child = children[i];
|
|
if (child is UsingDirectiveIntermediateNode)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
_namespace.Insert(i + 1, node);
|
|
}
|
|
|
|
public override void VisitDefault(IntermediateNode node)
|
|
{
|
|
if (node is MemberDeclarationIntermediateNode)
|
|
{
|
|
_class.Add(node);
|
|
return;
|
|
}
|
|
|
|
_method.Add(node);
|
|
}
|
|
}
|
|
}
|
|
}
|