aspnetcore/tools/CodeGenerator/FeatureCollectionGenerator.cs

197 lines
6.2 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 CodeGenerator
{
public static class FeatureCollectionGenerator
{
public static string GenerateFile(string namespaceName, string className, string[] allFeatures, string[] implementedFeatures, string extraUsings, string fallbackFeatures)
{
// NOTE: This list MUST always match the set of feature interfaces implemented by TransportConnection.
// See also: src/Kestrel/Http/TransportConnection.FeatureCollection.cs
var features = allFeatures.Select((type, index) => new KnownFeature
{
Name = type,
Index = index
});
return $@"// 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;
using System.Collections.Generic;
{extraUsings}
namespace {namespaceName}
{{
public partial class {className} : IFeatureCollection
{{{Each(features, feature => $@"
private static readonly Type {feature.Name}Type = typeof({feature.Name});")}
{Each(features, feature => $@"
private object _current{feature.Name};")}
private int _featureRevision;
private List<KeyValuePair<Type, object>> MaybeExtra;
private void FastReset()
{{{Each(implementedFeatures, feature => $@"
_current{feature} = this;")}
{Each(allFeatures.Where(f => !implementedFeatures.Contains(f)), feature => $@"
_current{feature} = null;")}
}}
// Internal for testing
internal void ResetFeatureCollection()
{{
FastReset();
MaybeExtra?.Clear();
_featureRevision++;
}}
private object ExtraFeatureGet(Type key)
{{
if (MaybeExtra == null)
{{
return null;
}}
for (var i = 0; i < MaybeExtra.Count; i++)
{{
var kv = MaybeExtra[i];
if (kv.Key == key)
{{
return kv.Value;
}}
}}
return null;
}}
private void ExtraFeatureSet(Type key, object value)
{{
if (MaybeExtra == null)
{{
MaybeExtra = new List<KeyValuePair<Type, object>>(2);
}}
for (var i = 0; i < MaybeExtra.Count; i++)
{{
if (MaybeExtra[i].Key == key)
{{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}}
}}
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}}
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
object IFeatureCollection.this[Type key]
{{
get
{{
object feature = null;{Each(features, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type)
{{
feature = _current{feature.Name};
}}")}
else if (MaybeExtra != null)
{{
feature = ExtraFeatureGet(key);
}}
return feature{(string.IsNullOrEmpty(fallbackFeatures) ? "" : $" ?? {fallbackFeatures}[key]")};
}}
set
{{
_featureRevision++;
{Each(features, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type)
{{
_current{feature.Name} = value;
}}")}
else
{{
ExtraFeatureSet(key, value);
}}
}}
}}
TFeature IFeatureCollection.Get<TFeature>()
{{
TFeature feature = default;{Each(features, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
{{
feature = (TFeature)_current{feature.Name};
}}")}
else if (MaybeExtra != null)
{{
feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
}}{(string.IsNullOrEmpty(fallbackFeatures) ? "" : $@"
if (feature == null)
{{
feature = {fallbackFeatures}.Get<TFeature>();
}}")}
return feature;
}}
void IFeatureCollection.Set<TFeature>(TFeature feature)
{{
_featureRevision++;{Each(features, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
{{
_current{feature.Name} = feature;
}}")}
else
{{
ExtraFeatureSet(typeof(TFeature), feature);
}}
}}
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{{{Each(features, feature => $@"
if (_current{feature.Name} != null)
{{
yield return new KeyValuePair<Type, object>({feature.Name}Type, _current{feature.Name});
}}")}
if (MaybeExtra != null)
{{
foreach (var item in MaybeExtra)
{{
yield return item;
}}
}}
}}
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
}}
}}
";
}
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
{
return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : "";
}
private class KnownFeature
{
public string Name;
public int Index;
}
}
}