aspnetcore/src/Microsoft.AspNetCore.Razor..../DefaultBoundAttributeDescri...

210 lines
7.5 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;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultBoundAttributeDescriptorBuilder : BoundAttributeDescriptorBuilder
{
private static readonly IReadOnlyDictionary<string, string> PrimitiveDisplayTypeNameLookups = new Dictionary<string, string>(StringComparer.Ordinal)
{
[typeof(byte).FullName] = "byte",
[typeof(sbyte).FullName] = "sbyte",
[typeof(int).FullName] = "int",
[typeof(uint).FullName] = "uint",
[typeof(short).FullName] = "short",
[typeof(ushort).FullName] = "ushort",
[typeof(long).FullName] = "long",
[typeof(ulong).FullName] = "ulong",
[typeof(float).FullName] = "float",
[typeof(double).FullName] = "double",
[typeof(char).FullName] = "char",
[typeof(bool).FullName] = "bool",
[typeof(object).FullName] = "object",
[typeof(string).FullName] = "string",
[typeof(decimal).FullName] = "decimal",
};
private readonly DefaultTagHelperDescriptorBuilder _parent;
private readonly string _kind;
private readonly Dictionary<string, string> _metadata;
private RazorDiagnosticCollection _diagnostics;
public DefaultBoundAttributeDescriptorBuilder(DefaultTagHelperDescriptorBuilder parent, string kind)
{
_parent = parent;
_kind = kind;
_metadata = new Dictionary<string, string>();
}
public override string Name { get; set; }
public override string TypeName { get; set; }
public override bool IsEnum { get; set; }
public override bool IsDictionary { get; set; }
public override string IndexerAttributeNamePrefix { get; set; }
public override string IndexerValueTypeName { get; set; }
public override string Documentation { get; set; }
public override string DisplayName { get; set; }
public override IDictionary<string, string> Metadata => _metadata;
public override RazorDiagnosticCollection Diagnostics
{
get
{
if (_diagnostics == null)
{
_diagnostics = new RazorDiagnosticCollection();
}
return _diagnostics;
}
}
public BoundAttributeDescriptor Build()
{
var validationDiagnostics = Validate();
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
if (_diagnostics != null)
{
diagnostics.UnionWith(_diagnostics);
}
var descriptor = new DefaultBoundAttributeDescriptor(
_kind,
Name,
TypeName,
IsEnum,
IsDictionary,
IndexerAttributeNamePrefix,
IndexerValueTypeName,
Documentation,
GetDisplayName(),
new Dictionary<string, string>(Metadata),
diagnostics.ToArray());
return descriptor;
}
private string GetDisplayName()
{
if (DisplayName != null)
{
return DisplayName;
}
var parentTypeName = _parent.GetTypeName();
var propertyName = this.GetPropertyName();
if (TypeName != null &&
propertyName != null &&
parentTypeName != null)
{
// This looks like a normal c# property, so lets compute a display name based on that.
if (!PrimitiveDisplayTypeNameLookups.TryGetValue(TypeName, out var simpleTypeName))
{
simpleTypeName = TypeName;
}
return $"{simpleTypeName} {parentTypeName}.{propertyName}";
}
return Name;
}
private IEnumerable<RazorDiagnostic> Validate()
{
// data-* attributes are explicitly not implemented by user agents and are not intended for use on
// the server; therefore it's invalid for TagHelpers to bind to them.
const string DataDashPrefix = "data-";
if (string.IsNullOrWhiteSpace(Name))
{
if (IndexerAttributeNamePrefix == null)
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
_parent.GetDisplayName(),
GetDisplayName());
yield return diagnostic;
}
}
else
{
if (Name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith(
_parent.GetDisplayName(),
GetDisplayName(),
Name);
yield return diagnostic;
}
foreach (var character in Name)
{
if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName(
_parent.GetDisplayName(),
GetDisplayName(),
Name,
character);
yield return diagnostic;
}
}
}
if (IndexerAttributeNamePrefix != null)
{
if (IndexerAttributeNamePrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefixStartsWith(
_parent.GetDisplayName(),
GetDisplayName(),
IndexerAttributeNamePrefix);
yield return diagnostic;
}
else if (IndexerAttributeNamePrefix.Length > 0 && string.IsNullOrWhiteSpace(IndexerAttributeNamePrefix))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
_parent.GetDisplayName(),
GetDisplayName());
yield return diagnostic;
}
else
{
foreach (var character in IndexerAttributeNamePrefix)
{
if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix(
_parent.GetDisplayName(),
GetDisplayName(),
IndexerAttributeNamePrefix,
character);
yield return diagnostic;
}
}
}
}
}
}
}