// 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;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Parser.TagHelpers;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Text;
namespace Microsoft.AspNet.Mvc.Razor
{
///
/// A subtype of that uses to support inheritance of tag
/// helpers from _ViewStart files.
///
public class MvcRazorParser : RazorParser
{
private readonly IEnumerable _viewStartDirectiveDescriptors;
///
/// Initializes a new instance of .
///
/// The to copy properties from.
/// The s that are inherited
/// from parsed pages from _ViewStart files.
/// The inherited by
/// default by all Razor pages in the application.
public MvcRazorParser([NotNull] RazorParser parser,
[NotNull] IReadOnlyList inheritedCodeTrees,
[NotNull] IReadOnlyList defaultInheritedChunks)
: base(parser)
{
// Construct tag helper descriptors from @addTagHelper and @removeTagHelper chunks
_viewStartDirectiveDescriptors = GetTagHelperDescriptors(inheritedCodeTrees, defaultInheritedChunks);
}
///
protected override IEnumerable GetTagHelperDescriptors(
[NotNull] Block documentRoot,
[NotNull] ParserErrorSink errorSink)
{
var visitor = new ViewStartAddRemoveTagHelperVisitor(TagHelperDescriptorResolver,
_viewStartDirectiveDescriptors,
errorSink);
return visitor.GetDescriptors(documentRoot);
}
private static IEnumerable GetTagHelperDescriptors(
IReadOnlyList inheritedCodeTrees,
IReadOnlyList defaultInheritedChunks)
{
var descriptors = new List();
// For tag helpers, the @removeTagHelper only applies tag helpers that were added prior to it.
// Consequently we must visit tag helpers outside-in - furthest _ViewStart first and nearest one last. This
// is different from the behavior of chunk merging where we visit the nearest one first and ignore chunks
// that were previously visited.
var chunksFromViewStarts = inheritedCodeTrees.Reverse()
.SelectMany(tree => tree.Chunks);
var chunksInOrder = defaultInheritedChunks.Concat(chunksFromViewStarts);
foreach (var chunk in chunksInOrder)
{
var addHelperChunk = chunk as AddTagHelperChunk;
if (addHelperChunk != null)
{
var descriptor = new TagHelperDirectiveDescriptor(addHelperChunk.LookupText,
SourceLocation.Undefined,
TagHelperDirectiveType.AddTagHelper);
descriptors.Add(descriptor);
}
else
{
var removeHelperChunk = chunk as RemoveTagHelperChunk;
if (removeHelperChunk != null)
{
var descriptor = new TagHelperDirectiveDescriptor(removeHelperChunk.LookupText,
SourceLocation.Undefined,
TagHelperDirectiveType.RemoveTagHelper);
descriptors.Add(descriptor);
}
}
}
return descriptors;
}
private class ViewStartAddRemoveTagHelperVisitor : AddOrRemoveTagHelperSpanVisitor
{
private readonly IEnumerable _viewStartDirectiveDescriptors;
public ViewStartAddRemoveTagHelperVisitor(
ITagHelperDescriptorResolver descriptorResolver,
IEnumerable viewStartDirectiveDescriptors,
ParserErrorSink errorSink)
: base(descriptorResolver, errorSink)
{
_viewStartDirectiveDescriptors = viewStartDirectiveDescriptors;
}
protected override TagHelperDescriptorResolutionContext GetTagHelperDescriptorResolutionContext(
IEnumerable descriptors,
ParserErrorSink errorSink)
{
return base.GetTagHelperDescriptorResolutionContext(
_viewStartDirectiveDescriptors.Concat(descriptors),
errorSink);
}
}
}
}