// 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; using System.Linq; namespace Microsoft.AspNetCore.Razor.Language { /// /// A descriptor type for a directive that can be parsed by the Razor parser. /// public abstract class DirectiveDescriptor { /// /// Gets the description of the directive. /// /// /// The description is used for information purposes, and has no effect on parsing. /// public abstract string Description { get; } /// /// Gets the directive keyword without the leading @ token. /// public abstract string Directive { get; } /// /// Gets the display name of the directive. /// /// /// The display name is used for information purposes, and has no effect on parsing. /// public abstract string DisplayName { get; } /// /// Gets the kind of the directive. The kind determines whether or not a directive has an associated block. /// public abstract DirectiveKind Kind { get; } /// /// Gets the way a directive can be used. The usage determines how many, and where directives can exist per document. /// public abstract DirectiveUsage Usage { get; } /// /// Gets the list of directive tokens that can follow the directive keyword. /// public abstract IReadOnlyList Tokens { get; } /// /// Creates a new . /// /// The directive keyword. /// The directive kind. /// A for the created directive. public static DirectiveDescriptor CreateDirective(string directive, DirectiveKind kind) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, kind, configure: null); } /// /// Creates a new . /// /// The directive keyword. /// The directive kind. /// A configuration delegate for the directive. /// A for the created directive. public static DirectiveDescriptor CreateDirective(string directive, DirectiveKind kind, Action configure) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } var builder = new DefaultDirectiveDescriptorBuilder(directive, kind); configure?.Invoke(builder); return builder.Build(); } /// /// Creates a new with set to /// /// The directive keyword. /// A for the created directive. public static DirectiveDescriptor CreateSingleLineDirective(string directive) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.SingleLine, configure: null); } /// /// Creates a new with set to /// /// The directive keyword. /// A configuration delegate for the directive. /// A for the created directive. public static DirectiveDescriptor CreateSingleLineDirective(string directive, Action configure) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.SingleLine, configure); } /// /// Creates a new with set to /// /// The directive keyword. /// A for the created directive. public static DirectiveDescriptor CreateRazorBlockDirective(string directive) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.RazorBlock, configure: null); } /// /// Creates a new with set to /// /// The directive keyword. /// A configuration delegate for the directive. /// A for the created directive. public static DirectiveDescriptor CreateRazorBlockDirective(string directive, Action configure) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.RazorBlock, configure); } /// /// Creates a new with set to /// /// The directive keyword. /// A for the created directive. public static DirectiveDescriptor CreateCodeBlockDirective(string directive) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.CodeBlock, configure: null); } /// /// Creates a new with set to /// /// The directive keyword. /// A configuration delegate for the directive. /// A for the created directive. public static DirectiveDescriptor CreateCodeBlockDirective(string directive, Action configure) { if (directive == null) { throw new ArgumentNullException(nameof(directive)); } return CreateDirective(directive, DirectiveKind.CodeBlock, configure); } private class DefaultDirectiveDescriptorBuilder : IDirectiveDescriptorBuilder { public DefaultDirectiveDescriptorBuilder(string name, DirectiveKind kind) { Directive = name; Kind = kind; Tokens = new List(); } public string Description { get; set; } public string Directive { get; } public string DisplayName { get; set; } public DirectiveKind Kind { get; } public DirectiveUsage Usage { get; set; } public IList Tokens { get; } public DirectiveDescriptor Build() { if (Directive.Length == 0) { throw new InvalidOperationException(Resources.FormatDirectiveDescriptor_InvalidDirectiveKeyword(Directive)); } for (var i = 0; i < Directive.Length; i++) { if (!char.IsLetter(Directive[i])) { throw new InvalidOperationException(Resources.FormatDirectiveDescriptor_InvalidDirectiveKeyword(Directive)); } } var foundOptionalToken = false; for (var i = 0; i < Tokens.Count; i++) { var token = Tokens[i]; foundOptionalToken |= token.Optional; if (foundOptionalToken && !token.Optional) { throw new InvalidOperationException(Resources.DirectiveDescriptor_InvalidNonOptionalToken); } } return new DefaultDirectiveDescriptor(Directive, Kind, Usage, Tokens.ToArray(), DisplayName, Description); } } private class DefaultDirectiveDescriptor : DirectiveDescriptor { public DefaultDirectiveDescriptor( string directive, DirectiveKind kind, DirectiveUsage usage, DirectiveTokenDescriptor[] tokens, string displayName, string description) { Directive = directive; Kind = kind; Usage = usage; Tokens = tokens; DisplayName = displayName; Description = description; } public override string Description { get; } public override string Directive { get; } public override string DisplayName { get; } public override DirectiveKind Kind { get; } public override DirectiveUsage Usage { get; } public override IReadOnlyList Tokens { get; } } } }