Razor TagHelperDescriptor Hashing (#24551)

* Razor TagHelperDescriptor Hashing and Caching

1/2 PRs for https://github.com/dotnet/aspnetcore/issues/23170

The other PR will be in aspnetcore-tooling to utilize these changes.

https://github.com/dotnet/aspnetcore-tooling/pull/2307
This commit is contained in:
Tanay Parikh 2020-08-04 13:06:42 -07:00 committed by GitHub
parent cfd20ad2d7
commit 29ceed2b2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1028 additions and 257 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -56,10 +56,20 @@ namespace Microsoft.AspNetCore.Razor.Language
}
var hash = HashCodeCombiner.Start();
hash.Add(descriptor.Kind);
hash.Add(descriptor.Kind, StringComparer.Ordinal);
hash.Add(descriptor.Name, StringComparer.Ordinal);
if (descriptor.BoundAttributeParameters != null)
{
for (var i = 0; i < descriptor.BoundAttributeParameters.Count; i++)
{
hash.Add(descriptor.BoundAttributeParameters[i]);
}
}
hash.Add(descriptor.Metadata.Count);
return hash.CombinedHash;
}
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -51,8 +51,10 @@ namespace Microsoft.AspNetCore.Razor.Language
}
var hash = HashCodeCombiner.Start();
hash.Add(descriptor.Kind);
hash.Add(descriptor.Kind, StringComparer.Ordinal);
hash.Add(descriptor.Name, StringComparer.Ordinal);
hash.Add(descriptor.TypeName, StringComparer.Ordinal);
hash.Add(descriptor.Metadata?.Count);
return hash.CombinedHash;
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -39,11 +39,19 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(descriptor));
}
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Directive, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Kind);
var hash = HashCodeCombiner.Start();
hash.Add(descriptor.Directive, StringComparer.Ordinal);
hash.Add(descriptor.Kind);
return hashCodeCombiner.CombinedHash;
if (descriptor.Tokens != null)
{
for (var i = 0; i < descriptor.Tokens.Count; i++)
{
hash.Add(descriptor.Tokens[i]);
}
}
return hash.CombinedHash;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains the Razor parser and code generation infrastructure.</Description>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -57,6 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var hash = HashCodeCombiner.Start();
hash.Add(descriptor.Name, StringComparer.Ordinal);
hash.Add(descriptor.Value, StringComparer.Ordinal);
return hash.CombinedHash;
}

View File

@ -120,8 +120,50 @@ namespace Microsoft.AspNetCore.Razor.Language
hash.Add(descriptor.Kind, StringComparer.Ordinal);
hash.Add(descriptor.AssemblyName, StringComparer.Ordinal);
hash.Add(descriptor.Name, StringComparer.Ordinal);
hash.Add(descriptor.DisplayName, StringComparer.Ordinal);
hash.Add(descriptor.CaseSensitive ? 1 : 0);
if (descriptor.BoundAttributes != null)
{
for (var i = 0; i < descriptor.BoundAttributes.Count; i++)
{
hash.Add(descriptor.BoundAttributes[i]);
}
}
if (descriptor.TagMatchingRules != null)
{
for (var i = 0; i < descriptor.TagMatchingRules.Count; i++)
{
hash.Add(descriptor.TagMatchingRules[i]);
}
}
if (descriptor.AllowedChildTags != null)
{
for (var i = 0; i < descriptor.AllowedChildTags.Count; i++)
{
hash.Add(descriptor.AllowedChildTags[i]);
}
}
if (descriptor.Diagnostics != null)
{
for (var i = 0; i < descriptor.Diagnostics.Count; i++)
{
hash.Add(descriptor.Diagnostics[i]);
}
}
if (descriptor.Metadata != null)
{
foreach (var kvp in descriptor.Metadata)
{
hash.Add(kvp.Value, StringComparer.Ordinal);
}
}
return hash.CombinedHash;
}
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -48,8 +48,17 @@ namespace Microsoft.AspNetCore.Razor.Language
var hash = HashCodeCombiner.Start();
hash.Add(rule.TagName, StringComparer.Ordinal);
hash.Add(rule.ParentTag, StringComparer.Ordinal);
if (rule.Attributes != null)
{
for (var i = 0; i < rule.Attributes.Count; ++i)
{
hash.Add(rule.Attributes[i]);
}
}
return hash.CombinedHash;
}
}
}
}

View File

@ -13,15 +13,31 @@
<BuildHelixPayload>false</BuildHelixPayload>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="TestFiles\**\*" />
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Razor.Language" />
<Reference Include="Newtonsoft.Json" Version="12.0.3" />
<ProjectReference Include="..\..\test\Microsoft.AspNetCore.Razor.Test.Common\Microsoft.AspNetCore.Razor.Test.Common.csproj" />
<ProjectReference Include="..\..\test\Microsoft.AspNetCore.Razor.Test.ComponentShim\Microsoft.AspNetCore.Razor.Test.ComponentShim.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(SharedSourceRoot)RazorShared\TagHelperDescriptorJsonConverter.cs">
<Link>Shared\TagHelperDescriptorJsonConverter.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)RazorShared\RazorDiagnosticJsonConverter.cs">
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)RazorShared\JsonReaderExtensions.cs">
<Link>Shared\JsonReaderExtensions.cs</Link>
</Compile>
<EmbeddedResource Include="$(SharedSourceRoot)RazorShared\taghelpers.json">
<Link>TestFiles\taghelpers.json</Link>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TestFiles\**\*" />
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,208 @@
// 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.IO;
using System.Linq;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Language
{
public class TagHelperDescriptorComparerTest
{
private static readonly TestFile TagHelpersTestFile = TestFile.Create("TestFiles/taghelpers.json", typeof(TagHelperDescriptorComparerTest));
[Fact]
public void GetHashCode_SameTagHelperDescriptors_HashCodeMatches()
{
// Arrange
var descriptor1 = CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
});
var descriptor2 = CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
});
// Act
var hashCode1 = descriptor1.GetHashCode();
var hashCode2 = descriptor2.GetHashCode();
// Assert
Assert.Equal(hashCode1, hashCode2);
}
[Fact]
public void GetHashCode_FQNAndNameTagHelperDescriptors_HashCodeDoesNotMatch()
{
// Arrange
var descriptorName = CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
tagMatchingRuleName: "Input",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
});
var descriptorFQN = CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
tagMatchingRuleName: "Microsoft.AspNetCore.Components.Forms.Input",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
});
// Act
var hashCodeName = descriptorName.GetHashCode();
var hashCodeFQN = descriptorFQN.GetHashCode();
// Assert
Assert.NotEqual(hashCodeName, hashCodeFQN);
}
[Fact]
public void GetHashCode_DifferentTagHelperDescriptors_HashCodeDoesNotMatch()
{
// Arrange
var counterTagHelper = CreateTagHelperDescriptor(
tagName: "Counter",
typeName: "CounterTagHelper",
assemblyName: "Components.Component",
tagMatchingRuleName: "Input",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("IncrementBy")
.PropertyName("IncrementBy")
.TypeName("System.Int32"),
});
var inputTagHelper = CreateTagHelperDescriptor(
tagName: "input",
typeName: "InputTagHelper",
assemblyName: "TestAssembly",
tagMatchingRuleName: "Microsoft.AspNetCore.Components.Forms.Input",
attributes: new Action<BoundAttributeDescriptorBuilder>[]
{
builder => builder
.Name("value")
.PropertyName("FooProp")
.TypeName("System.String"),
});
// Act
var hashCodeCounter = counterTagHelper.GetHashCode();
var hashCodeInput = inputTagHelper.GetHashCode();
// Assert
Assert.NotEqual(hashCodeCounter, hashCodeInput);
}
[Fact]
public void GetHashCode_AllTagHelpers_NoHashCodeCollisions()
{
// Arrange
var tagHelpers = ReadTagHelpers(TagHelpersTestFile.OpenRead());
// Act
var hashes = new HashSet<int>(tagHelpers.Select(t => t.GetHashCode()));
// Assert
Assert.Equal(hashes.Count(), tagHelpers.Count);
}
[Fact]
public void GetHashCode_DuplicateTagHelpers_NoHashCodeCollisions()
{
// Arrange
var tagHelpers = new List<TagHelperDescriptor>();
var tagHelpersPerBatch = -1;
// Reads 5 copies of the TagHelpers (with 5x references)
// This ensures we don't have any dependencies on reference based GetHashCode
for (var i = 0; i < 5; ++i)
{
var tagHelpersBatch = ReadTagHelpers(TagHelpersTestFile.OpenRead());
tagHelpers.AddRange(tagHelpersBatch);
tagHelpersPerBatch = tagHelpersBatch.Count;
}
// Act
var hashes = new HashSet<int>(tagHelpers.Select(t => t.GetHashCode()));
// Assert
// Only 1 batch of taghelpers should remain after we filter by hash
Assert.Equal(hashes.Count(), tagHelpersPerBatch);
}
private static TagHelperDescriptor CreateTagHelperDescriptor(
string tagName,
string typeName,
string assemblyName,
string tagMatchingRuleName = null,
IEnumerable<Action<BoundAttributeDescriptorBuilder>> attributes = null)
{
var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName) as DefaultTagHelperDescriptorBuilder;
builder.TypeName(typeName);
if (attributes != null)
{
foreach (var attributeBuilder in attributes)
{
builder.BoundAttributeDescriptor(attributeBuilder);
}
}
builder.TagMatchingRuleDescriptor(ruleBuilder => ruleBuilder.RequireTagName(tagMatchingRuleName ?? tagName));
var descriptor = builder.Build();
return descriptor;
}
private IReadOnlyList<TagHelperDescriptor> ReadTagHelpers(Stream stream)
{
var serializer = new JsonSerializer();
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
IReadOnlyList<TagHelperDescriptor> result;
using var streamReader = new StreamReader(stream);
using (var reader = new JsonTextReader(streamReader))
{
result = serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
}
stream.Dispose();
return result;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.Tools
@ -223,4 +222,4 @@ namespace Microsoft.AspNetCore.Razor.Tools
}
}
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.Tools

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This assembly contains infrastructure supporting Razor MSBuild integration.</Description>
@ -33,6 +33,9 @@
<Compile Include="$(SharedSourceRoot)RazorShared\RazorDiagnosticJsonConverter.cs">
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)RazorShared\JsonReaderExtensions.cs">
<Link>Shared\JsonReaderExtensions.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
</ItemGroup>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
@ -16,15 +16,22 @@
<ItemGroup>
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
<None Include="MSN.cshtml" CopyToOutputDirectory="PreserveNewest" />
<None Include="taghelpers.json" CopyToOutputDirectory="PreserveNewest" />
<None Include="BlazorServerTagHelpers.razor" CopyToOutputDirectory="PreserveNewest" />
<Compile Include="$(SharedSourceRoot)RazorShared\TagHelperDescriptorJsonConverter.cs">
<Link>Shared\TagHelperDescriptorJsonConverter.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)RazorShared\RazorDiagnosticJsonConverter.cs">
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
</Compile>
<Compile Include="$(SharedSourceRoot)RazorShared\JsonReaderExtensions.cs">
<Link>Shared\JsonReaderExtensions.cs</Link>
</Compile>
<None Include="MSN.cshtml" CopyToOutputDirectory="PreserveNewest" />
<None Include="$(SharedSourceRoot)RazorShared\taghelpers.json" CopyToOutputDirectory="PreserveNewest">
<Link>taghelpers.json</Link>
</None>
<None Include="BlazorServerTagHelpers.razor" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -8,7 +8,6 @@ using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
using Newtonsoft.Json;
using static Microsoft.AspNetCore.Razor.Language.DefaultRazorTagHelperBinderPhase;

View File

@ -0,0 +1,58 @@
// 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.Diagnostics;
using Newtonsoft.Json;
namespace Microsoft.CodeAnalysis.Razor.Serialization
{
internal static class JsonReaderExtensions
{
public static bool ReadTokenAndAdvance(this JsonReader reader, JsonToken expectedTokenType, out object value)
{
value = reader.Value;
return reader.TokenType == expectedTokenType && reader.Read();
}
public static void ReadProperties(this JsonReader reader, Action<string> onProperty)
{
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
var propertyName = reader.Value.ToString();
onProperty(propertyName);
break;
case JsonToken.EndObject:
return;
}
}
}
public static string ReadNextStringProperty(this JsonReader reader, string propertyName)
{
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
Debug.Assert(reader.Value.ToString() == propertyName);
if (reader.Read())
{
var value = (string)reader.Value;
return value;
}
else
{
return null;
}
}
}
throw new JsonSerializationException($"Could not find string property '{propertyName}'.");
}
}
}

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
namespace Microsoft.CodeAnalysis.Razor.Serialization
{
internal class RazorDiagnosticJsonConverter : JsonConverter
{

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.CodeAnalysis.Razor.Serialization
{
@ -25,56 +24,54 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
return null;
}
var descriptor = JObject.Load(reader);
var descriptorKind = descriptor[nameof(TagHelperDescriptor.Kind)].Value<string>();
var typeName = descriptor[nameof(TagHelperDescriptor.Name)].Value<string>();
var assemblyName = descriptor[nameof(TagHelperDescriptor.AssemblyName)].Value<string>();
var tagMatchingRules = descriptor[nameof(TagHelperDescriptor.TagMatchingRules)].Value<JArray>();
var boundAttributes = descriptor[nameof(TagHelperDescriptor.BoundAttributes)].Value<JArray>();
var childTags = descriptor[nameof(TagHelperDescriptor.AllowedChildTags)].Value<JArray>();
var documentation = descriptor[nameof(TagHelperDescriptor.Documentation)].Value<string>();
var tagOutputHint = descriptor[nameof(TagHelperDescriptor.TagOutputHint)].Value<string>();
var caseSensitive = descriptor[nameof(TagHelperDescriptor.CaseSensitive)].Value<bool>();
var diagnostics = descriptor[nameof(TagHelperDescriptor.Diagnostics)].Value<JArray>();
var metadata = descriptor[nameof(TagHelperDescriptor.Metadata)].Value<JObject>();
// Required tokens (order matters)
var descriptorKind = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Kind));
var typeName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Name));
var assemblyName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.AssemblyName));
var builder = TagHelperDescriptorBuilder.Create(descriptorKind, typeName, assemblyName);
builder.Documentation = documentation;
builder.TagOutputHint = tagOutputHint;
builder.CaseSensitive = caseSensitive;
foreach (var tagMatchingRule in tagMatchingRules)
reader.ReadProperties(propertyName =>
{
var rule = tagMatchingRule.Value<JObject>();
builder.TagMatchingRule(b => ReadTagMatchingRule(b, rule, serializer));
}
foreach (var boundAttribute in boundAttributes)
{
var attribute = boundAttribute.Value<JObject>();
builder.BindAttribute(b => ReadBoundAttribute(b, attribute, serializer));
}
foreach (var childTag in childTags)
{
var tag = childTag.Value<JObject>();
builder.AllowChildTag(childTagBuilder => ReadAllowedChildTag(childTagBuilder, tag, serializer));
}
foreach (var diagnostic in diagnostics)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
switch (propertyName)
{
case nameof(TagHelperDescriptor.Documentation):
if (reader.Read())
{
var documentation = (string)reader.Value;
builder.Documentation = documentation;
}
break;
case nameof(TagHelperDescriptor.TagOutputHint):
if (reader.Read())
{
var tagOutputHint = (string)reader.Value;
builder.TagOutputHint = tagOutputHint;
}
break;
case nameof(TagHelperDescriptor.CaseSensitive):
if (reader.Read())
{
var caseSensitive = (bool)reader.Value;
builder.CaseSensitive = caseSensitive;
}
break;
case nameof(TagHelperDescriptor.TagMatchingRules):
ReadTagMatchingRules(reader, builder);
break;
case nameof(TagHelperDescriptor.BoundAttributes):
ReadBoundAttributes(reader, builder);
break;
case nameof(TagHelperDescriptor.AllowedChildTags):
ReadAllowedChildTags(reader, builder);
break;
case nameof(TagHelperDescriptor.Diagnostics):
ReadDiagnostics(reader, builder.Diagnostics);
break;
case nameof(TagHelperDescriptor.Metadata):
ReadMetadata(reader, builder.Metadata);
break;
}
});
return builder.Build();
}
@ -94,11 +91,17 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
writer.WritePropertyName(nameof(TagHelperDescriptor.AssemblyName));
writer.WriteValue(tagHelper.AssemblyName);
writer.WritePropertyName(nameof(TagHelperDescriptor.Documentation));
writer.WriteValue(tagHelper.Documentation);
if (tagHelper.Documentation != null)
{
writer.WritePropertyName(nameof(TagHelperDescriptor.Documentation));
writer.WriteValue(tagHelper.Documentation);
}
writer.WritePropertyName(nameof(TagHelperDescriptor.TagOutputHint));
writer.WriteValue(tagHelper.TagOutputHint);
if (tagHelper.TagOutputHint != null)
{
writer.WritePropertyName(nameof(TagHelperDescriptor.TagOutputHint));
writer.WriteValue(tagHelper.TagOutputHint);
}
writer.WritePropertyName(nameof(TagHelperDescriptor.CaseSensitive));
writer.WriteValue(tagHelper.CaseSensitive);
@ -111,24 +114,33 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.BoundAttributes));
writer.WriteStartArray();
foreach (var boundAttribute in tagHelper.BoundAttributes)
if (tagHelper.BoundAttributes != null && tagHelper.BoundAttributes.Count > 0)
{
WriteBoundAttribute(writer, boundAttribute, serializer);
writer.WritePropertyName(nameof(TagHelperDescriptor.BoundAttributes));
writer.WriteStartArray();
foreach (var boundAttribute in tagHelper.BoundAttributes)
{
WriteBoundAttribute(writer, boundAttribute, serializer);
}
writer.WriteEndArray();
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.AllowedChildTags));
writer.WriteStartArray();
foreach (var allowedChildTag in tagHelper.AllowedChildTags)
if (tagHelper.AllowedChildTags != null && tagHelper.AllowedChildTags.Count > 0)
{
WriteAllowedChildTags(writer, allowedChildTag, serializer);
writer.WritePropertyName(nameof(TagHelperDescriptor.AllowedChildTags));
writer.WriteStartArray();
foreach (var allowedChildTag in tagHelper.AllowedChildTags)
{
WriteAllowedChildTags(writer, allowedChildTag, serializer);
}
writer.WriteEndArray();
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagHelperDescriptor.Diagnostics));
serializer.Serialize(writer, tagHelper.Diagnostics);
if (tagHelper.Diagnostics != null && tagHelper.Diagnostics.Count > 0)
{
writer.WritePropertyName(nameof(TagHelperDescriptor.Diagnostics));
serializer.Serialize(writer, tagHelper.Diagnostics);
}
writer.WritePropertyName(nameof(TagHelperDescriptor.Metadata));
WriteMetadata(writer, tagHelper.Metadata);
@ -165,31 +177,49 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
writer.WritePropertyName(nameof(BoundAttributeDescriptor.TypeName));
writer.WriteValue(boundAttribute.TypeName);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEnum));
writer.WriteValue(boundAttribute.IsEnum);
if (boundAttribute.IsEnum)
{
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IsEnum));
writer.WriteValue(boundAttribute.IsEnum);
}
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
writer.WriteValue(boundAttribute.IndexerNamePrefix);
if (boundAttribute.IndexerNamePrefix != null)
{
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
writer.WriteValue(boundAttribute.IndexerNamePrefix);
}
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerTypeName));
writer.WriteValue(boundAttribute.IndexerTypeName);
if (boundAttribute.IndexerTypeName != null)
{
writer.WritePropertyName(nameof(BoundAttributeDescriptor.IndexerTypeName));
writer.WriteValue(boundAttribute.IndexerTypeName);
}
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Documentation));
writer.WriteValue(boundAttribute.Documentation);
if (boundAttribute.Documentation != null)
{
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Documentation));
writer.WriteValue(boundAttribute.Documentation);
}
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttribute.Diagnostics);
if (boundAttribute.Diagnostics != null && boundAttribute.Diagnostics.Count > 0)
{
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttribute.Diagnostics);
}
writer.WritePropertyName(nameof(BoundAttributeDescriptor.Metadata));
WriteMetadata(writer, boundAttribute.Metadata);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.BoundAttributeParameters));
writer.WriteStartArray();
foreach (var boundAttributeParameter in boundAttribute.BoundAttributeParameters)
if (boundAttribute.BoundAttributeParameters != null && boundAttribute.BoundAttributeParameters.Count > 0)
{
WriteBoundAttributeParameter(writer, boundAttributeParameter, serializer);
writer.WritePropertyName(nameof(BoundAttributeDescriptor.BoundAttributeParameters));
writer.WriteStartArray();
foreach (var boundAttributeParameter in boundAttribute.BoundAttributeParameters)
{
WriteBoundAttributeParameter(writer, boundAttributeParameter, serializer);
}
writer.WriteEndArray();
}
writer.WriteEndArray();
writer.WriteEndObject();
}
@ -198,23 +228,29 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Kind));
writer.WriteValue(boundAttributeParameter.Kind);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Name));
writer.WriteValue(boundAttributeParameter.Name);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.TypeName));
writer.WriteValue(boundAttributeParameter.TypeName);
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.IsEnum));
writer.WriteValue(boundAttributeParameter.IsEnum);
if (boundAttributeParameter.IsEnum != default)
{
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.IsEnum));
writer.WriteValue(boundAttributeParameter.IsEnum);
}
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Documentation));
writer.WriteValue(boundAttributeParameter.Documentation);
if (boundAttributeParameter.Documentation != null)
{
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Documentation));
writer.WriteValue(boundAttributeParameter.Documentation);
}
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttributeParameter.Diagnostics);
if (boundAttributeParameter.Diagnostics != null && boundAttributeParameter.Diagnostics.Count > 0)
{
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Diagnostics));
serializer.Serialize(writer, boundAttributeParameter.Diagnostics);
}
writer.WritePropertyName(nameof(BoundAttributeParameterDescriptor.Metadata));
WriteMetadata(writer, boundAttributeParameter.Metadata);
@ -240,22 +276,34 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagName));
writer.WriteValue(ruleDescriptor.TagName);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.ParentTag));
writer.WriteValue(ruleDescriptor.ParentTag);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagStructure));
writer.WriteValue(ruleDescriptor.TagStructure);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Attributes));
writer.WriteStartArray();
foreach (var requiredAttribute in ruleDescriptor.Attributes)
if (ruleDescriptor.ParentTag != null)
{
WriteRequiredAttribute(writer, requiredAttribute, serializer);
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.ParentTag));
writer.WriteValue(ruleDescriptor.ParentTag);
}
writer.WriteEndArray();
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Diagnostics));
serializer.Serialize(writer, ruleDescriptor.Diagnostics);
if (ruleDescriptor.TagStructure != default)
{
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.TagStructure));
writer.WriteValue(ruleDescriptor.TagStructure);
}
if (ruleDescriptor.Attributes != null && ruleDescriptor.Attributes.Count > 0)
{
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Attributes));
writer.WriteStartArray();
foreach (var requiredAttribute in ruleDescriptor.Attributes)
{
WriteRequiredAttribute(writer, requiredAttribute, serializer);
}
writer.WriteEndArray();
}
if (ruleDescriptor.Diagnostics != null && ruleDescriptor.Diagnostics.Count > 0)
{
writer.WritePropertyName(nameof(TagMatchingRuleDescriptor.Diagnostics));
serializer.Serialize(writer, ruleDescriptor.Diagnostics);
}
writer.WriteEndObject();
}
@ -267,176 +315,538 @@ namespace Microsoft.CodeAnalysis.Razor.Serialization
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Name));
writer.WriteValue(requiredAttribute.Name);
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.NameComparison));
writer.WriteValue(requiredAttribute.NameComparison);
if (requiredAttribute.NameComparison != default)
{
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.NameComparison));
writer.WriteValue(requiredAttribute.NameComparison);
}
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Value));
writer.WriteValue(requiredAttribute.Value);
if (requiredAttribute.Value != null)
{
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Value));
writer.WriteValue(requiredAttribute.Value);
}
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.ValueComparison));
writer.WriteValue(requiredAttribute.ValueComparison);
if (requiredAttribute.ValueComparison != default)
{
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.ValueComparison));
writer.WriteValue(requiredAttribute.ValueComparison);
}
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, requiredAttribute.Diagnostics);
if (requiredAttribute.Diagnostics != null && requiredAttribute.Diagnostics.Count > 0)
{
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Diagnostics));
serializer.Serialize(writer, requiredAttribute.Diagnostics);
}
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Metadata));
WriteMetadata(writer, requiredAttribute.Metadata);
if (requiredAttribute.Metadata != null && requiredAttribute.Metadata.Count > 0)
{
writer.WritePropertyName(nameof(RequiredAttributeDescriptor.Metadata));
WriteMetadata(writer, requiredAttribute.Metadata);
}
writer.WriteEndObject();
}
private static void ReadTagMatchingRule(TagMatchingRuleDescriptorBuilder builder, JObject rule, JsonSerializer serializer)
private static void ReadBoundAttributes(JsonReader reader, TagHelperDescriptorBuilder builder)
{
var tagName = rule[nameof(TagMatchingRuleDescriptor.TagName)].Value<string>();
var attributes = rule[nameof(TagMatchingRuleDescriptor.Attributes)].Value<JArray>();
var parentTag = rule[nameof(TagMatchingRuleDescriptor.ParentTag)].Value<string>();
var tagStructure = rule[nameof(TagMatchingRuleDescriptor.TagStructure)].Value<int>();
var diagnostics = rule[nameof(TagMatchingRuleDescriptor.Diagnostics)].Value<JArray>();
builder.TagName = tagName;
builder.ParentTag = parentTag;
builder.TagStructure = (TagStructure)tagStructure;
foreach (var attribute in attributes)
if (!reader.Read())
{
var attibuteValue = attribute.Value<JObject>();
builder.Attribute(b => ReadRequiredAttribute(b, attibuteValue, serializer));
return;
}
foreach (var diagnostic in diagnostics)
if (reader.TokenType != JsonToken.StartArray)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
return;
}
do
{
ReadBoundAttribute(reader, builder);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadRequiredAttribute(RequiredAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
private static void ReadBoundAttribute(JsonReader reader, TagHelperDescriptorBuilder builder)
{
var name = attribute[nameof(RequiredAttributeDescriptor.Name)].Value<string>();
var nameComparison = attribute[nameof(RequiredAttributeDescriptor.NameComparison)].Value<int>();
var value = attribute[nameof(RequiredAttributeDescriptor.Value)].Value<string>();
var valueComparison = attribute[nameof(RequiredAttributeDescriptor.ValueComparison)].Value<int>();
var diagnostics = attribute[nameof(RequiredAttributeDescriptor.Diagnostics)].Value<JArray>();
var metadata = attribute[nameof(RequiredAttributeDescriptor.Metadata)].Value<JObject>();
builder.Name = name;
builder.NameComparisonMode = (RequiredAttributeDescriptor.NameComparisonMode)nameComparison;
builder.Value = value;
builder.ValueComparisonMode = (RequiredAttributeDescriptor.ValueComparisonMode)valueComparison;
foreach (var diagnostic in diagnostics)
if (!reader.Read())
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
return;
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
if (reader.TokenType != JsonToken.StartObject)
{
builder.Metadata[item.Key] = item.Value;
return;
}
builder.BindAttribute(attribute =>
{
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(BoundAttributeDescriptor.Name):
if (reader.Read())
{
var name = (string)reader.Value;
attribute.Name = name;
}
break;
case nameof(BoundAttributeDescriptor.TypeName):
if (reader.Read())
{
var typeName = (string)reader.Value;
attribute.TypeName = typeName;
}
break;
case nameof(BoundAttributeDescriptor.Documentation):
if (reader.Read())
{
var documentation = (string)reader.Value;
attribute.Documentation = documentation;
}
break;
case nameof(BoundAttributeDescriptor.IndexerNamePrefix):
if (reader.Read())
{
var indexerNamePrefix = (string)reader.Value;
if (indexerNamePrefix != null)
{
attribute.IsDictionary = true;
attribute.IndexerAttributeNamePrefix = indexerNamePrefix;
}
}
break;
case nameof(BoundAttributeDescriptor.IndexerTypeName):
if (reader.Read())
{
var indexerTypeName = (string)reader.Value;
if (indexerTypeName != null)
{
attribute.IsDictionary = true;
attribute.IndexerValueTypeName = indexerTypeName;
}
}
break;
case nameof(BoundAttributeDescriptor.IsEnum):
if (reader.Read())
{
var isEnum = (bool)reader.Value;
attribute.IsEnum = isEnum;
}
break;
case nameof(BoundAttributeDescriptor.BoundAttributeParameters):
ReadBoundAttributeParameters(reader, attribute);
break;
case nameof(BoundAttributeDescriptor.Diagnostics):
ReadDiagnostics(reader, attribute.Diagnostics);
break;
case nameof(BoundAttributeDescriptor.Metadata):
ReadMetadata(reader, attribute.Metadata);
break;
}
});
});
}
private static void ReadAllowedChildTag(AllowedChildTagDescriptorBuilder builder, JObject childTag, JsonSerializer serializer)
private static void ReadBoundAttributeParameters(JsonReader reader, BoundAttributeDescriptorBuilder builder)
{
var name = childTag[nameof(AllowedChildTagDescriptor.Name)].Value<string>();
var displayName = childTag[nameof(AllowedChildTagDescriptor.DisplayName)].Value<string>();
var diagnostics = childTag[nameof(AllowedChildTagDescriptor.Diagnostics)].Value<JArray>();
builder.Name = name;
builder.DisplayName = displayName;
foreach (var diagnostic in diagnostics)
if (!reader.Read())
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
return;
}
if (reader.TokenType != JsonToken.StartArray)
{
return;
}
do
{
ReadBoundAttributeParameter(reader, builder);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadBoundAttribute(BoundAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
private static void ReadBoundAttributeParameter(JsonReader reader, BoundAttributeDescriptorBuilder builder)
{
var descriptorKind = attribute[nameof(BoundAttributeDescriptor.Kind)].Value<string>();
var name = attribute[nameof(BoundAttributeDescriptor.Name)].Value<string>();
var typeName = attribute[nameof(BoundAttributeDescriptor.TypeName)].Value<string>();
var isEnum = attribute[nameof(BoundAttributeDescriptor.IsEnum)].Value<bool>();
var indexerNamePrefix = attribute[nameof(BoundAttributeDescriptor.IndexerNamePrefix)].Value<string>();
var indexerTypeName = attribute[nameof(BoundAttributeDescriptor.IndexerTypeName)].Value<string>();
var documentation = attribute[nameof(BoundAttributeDescriptor.Documentation)].Value<string>();
var diagnostics = attribute[nameof(BoundAttributeDescriptor.Diagnostics)].Value<JArray>();
var metadata = attribute[nameof(BoundAttributeDescriptor.Metadata)].Value<JObject>();
var boundAttributeParameters = attribute[nameof(BoundAttributeDescriptor.BoundAttributeParameters)].Value<JArray>();
builder.Name = name;
builder.TypeName = typeName;
builder.Documentation = documentation;
if (indexerNamePrefix != null)
if (!reader.Read())
{
builder.AsDictionary(indexerNamePrefix, indexerTypeName);
return;
}
if (isEnum)
if (reader.TokenType != JsonToken.StartObject)
{
builder.IsEnum = true;
return;
}
foreach (var diagnostic in diagnostics)
builder.BindAttributeParameter(parameter =>
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
{
builder.Metadata[item.Key] = item.Value;
}
foreach (var boundAttributeParameter in boundAttributeParameters)
{
var parameter = boundAttributeParameter.Value<JObject>();
builder.BindAttributeParameter(b => ReadBoundAttributeParameter(b, parameter, serializer));
}
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(BoundAttributeParameterDescriptor.Name):
if (reader.Read())
{
var name = (string)reader.Value;
parameter.Name = name;
}
break;
case nameof(BoundAttributeParameterDescriptor.TypeName):
if (reader.Read())
{
var typeName = (string)reader.Value;
parameter.TypeName = typeName;
}
break;
case nameof(BoundAttributeParameterDescriptor.IsEnum):
if (reader.Read())
{
var isEnum = (bool)reader.Value;
parameter.IsEnum = isEnum;
}
break;
case nameof(BoundAttributeParameterDescriptor.Documentation):
if (reader.Read())
{
var documentation = (string)reader.Value;
parameter.Documentation = documentation;
}
break;
case nameof(BoundAttributeParameterDescriptor.Metadata):
ReadMetadata(reader, parameter.Metadata);
break;
case nameof(BoundAttributeParameterDescriptor.Diagnostics):
ReadDiagnostics(reader, parameter.Diagnostics);
break;
}
});
});
}
private static void ReadBoundAttributeParameter(BoundAttributeParameterDescriptorBuilder builder, JObject parameter, JsonSerializer serializer)
private static void ReadTagMatchingRules(JsonReader reader, TagHelperDescriptorBuilder builder)
{
var descriptorKind = parameter[nameof(BoundAttributeParameterDescriptor.Kind)].Value<string>();
var name = parameter[nameof(BoundAttributeParameterDescriptor.Name)].Value<string>();
var typeName = parameter[nameof(BoundAttributeParameterDescriptor.TypeName)].Value<string>();
var isEnum = parameter[nameof(BoundAttributeParameterDescriptor.IsEnum)].Value<bool>();
var documentation = parameter[nameof(BoundAttributeParameterDescriptor.Documentation)].Value<string>();
var diagnostics = parameter[nameof(BoundAttributeParameterDescriptor.Diagnostics)].Value<JArray>();
var metadata = parameter[nameof(BoundAttributeParameterDescriptor.Metadata)].Value<JObject>();
builder.Name = name;
builder.TypeName = typeName;
builder.Documentation = documentation;
if (isEnum)
if (!reader.Read())
{
builder.IsEnum = true;
return;
}
foreach (var diagnostic in diagnostics)
if (reader.TokenType != JsonToken.StartArray)
{
var diagnosticReader = diagnostic.CreateReader();
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
builder.Diagnostics.Add(diagnosticObject);
return;
}
var metadataReader = metadata.CreateReader();
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
foreach (var item in metadataValue)
do
{
builder.Metadata[item.Key] = item.Value;
ReadTagMatchingRule(reader, builder);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadTagMatchingRule(JsonReader reader, TagHelperDescriptorBuilder builder)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartObject)
{
return;
}
builder.TagMatchingRule(rule =>
{
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(TagMatchingRuleDescriptor.TagName):
if (reader.Read())
{
var tagName = (string)reader.Value;
rule.TagName = tagName;
}
break;
case nameof(TagMatchingRuleDescriptor.ParentTag):
if (reader.Read())
{
var parentTag = (string)reader.Value;
rule.ParentTag = parentTag;
}
break;
case nameof(TagMatchingRuleDescriptor.TagStructure):
rule.TagStructure = (TagStructure)reader.ReadAsInt32();
break;
case nameof(TagMatchingRuleDescriptor.Attributes):
ReadRequiredAttributeValues(reader, rule);
break;
case nameof(TagMatchingRuleDescriptor.Diagnostics):
ReadDiagnostics(reader, rule.Diagnostics);
break;
}
});
});
}
private static void ReadRequiredAttributeValues(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartArray)
{
return;
}
do
{
ReadRequiredAttribute(reader, builder);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadRequiredAttribute(JsonReader reader, TagMatchingRuleDescriptorBuilder builder)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartObject)
{
return;
}
builder.Attribute(attribute =>
{
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(RequiredAttributeDescriptor.Name):
if (reader.Read())
{
var name = (string)reader.Value;
attribute.Name = name;
}
break;
case nameof(RequiredAttributeDescriptor.NameComparison):
var nameComparison = (RequiredAttributeDescriptor.NameComparisonMode)reader.ReadAsInt32();
attribute.NameComparisonMode = nameComparison;
break;
case nameof(RequiredAttributeDescriptor.Value):
if (reader.Read())
{
var value = (string)reader.Value;
attribute.Value = value;
}
break;
case nameof(RequiredAttributeDescriptor.ValueComparison):
var valueComparison = (RequiredAttributeDescriptor.ValueComparisonMode)reader.ReadAsInt32();
attribute.ValueComparisonMode = valueComparison;
break;
case nameof(RequiredAttributeDescriptor.Diagnostics):
ReadDiagnostics(reader, attribute.Diagnostics);
break;
case nameof(RequiredAttributeDescriptor.Metadata):
ReadMetadata(reader, attribute.Metadata);
break;
}
});
});
}
private static void ReadAllowedChildTags(JsonReader reader, TagHelperDescriptorBuilder builder)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartArray)
{
return;
}
do
{
ReadAllowedChildTag(reader, builder);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadAllowedChildTag(JsonReader reader, TagHelperDescriptorBuilder builder)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartObject)
{
return;
}
builder.AllowChildTag(childTag =>
{
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(AllowedChildTagDescriptor.Name):
if (reader.Read())
{
var name = (string)reader.Value;
childTag.Name = name;
}
break;
case nameof(AllowedChildTagDescriptor.DisplayName):
if (reader.Read())
{
var displayName = (string)reader.Value;
childTag.DisplayName = displayName;
}
break;
case nameof(AllowedChildTagDescriptor.Diagnostics):
ReadDiagnostics(reader, childTag.Diagnostics);
break;
}
});
});
}
private static void ReadMetadata(JsonReader reader, IDictionary<string, string> metadata)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartObject)
{
return;
}
reader.ReadProperties(propertyName =>
{
if (reader.Read())
{
var value = (string)reader.Value;
metadata[propertyName] = value;
}
});
}
private static void ReadDiagnostics(JsonReader reader, RazorDiagnosticCollection diagnostics)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartArray)
{
return;
}
do
{
ReadDiagnostic(reader, diagnostics);
} while (reader.TokenType != JsonToken.EndArray);
}
private static void ReadDiagnostic(JsonReader reader, RazorDiagnosticCollection diagnostics)
{
if (!reader.Read())
{
return;
}
if (reader.TokenType != JsonToken.StartObject)
{
return;
}
string id = default;
int severity = default;
string message = default;
SourceSpan sourceSpan = default;
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(RazorDiagnostic.Id):
if (reader.Read())
{
id = (string)reader.Value;
}
break;
case nameof(RazorDiagnostic.Severity):
severity = reader.ReadAsInt32().Value;
break;
case "Message":
if (reader.Read())
{
message = (string)reader.Value;
}
break;
case nameof(RazorDiagnostic.Span):
sourceSpan = ReadSourceSpan(reader);
break;
}
});
var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
var diagnostic = RazorDiagnostic.Create(descriptor, sourceSpan);
diagnostics.Add(diagnostic);
}
private static SourceSpan ReadSourceSpan(JsonReader reader)
{
if (!reader.Read())
{
return SourceSpan.Undefined;
}
if (reader.TokenType != JsonToken.StartObject)
{
return SourceSpan.Undefined;
}
string filePath = default;
int absoluteIndex = default;
int lineIndex = default;
int characterIndex = default;
int length = default;
reader.ReadProperties(propertyName =>
{
switch (propertyName)
{
case nameof(SourceSpan.FilePath):
if (reader.Read())
{
filePath = (string)reader.Value;
}
break;
case nameof(SourceSpan.AbsoluteIndex):
absoluteIndex = reader.ReadAsInt32().Value;
break;
case nameof(SourceSpan.LineIndex):
lineIndex = reader.ReadAsInt32().Value;
break;
case nameof(SourceSpan.CharacterIndex):
characterIndex = reader.ReadAsInt32().Value;
break;
case nameof(SourceSpan.Length):
length = reader.ReadAsInt32().Value;
break;
}
});
var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
return sourceSpan;
}
}
}