| File: RuntimeSource\Configuration.Binder\Specs\TypeIndex.cs | Web Access |
| Project: src\src\Tools\ConfigurationSchemaGenerator\ConfigurationSchemaGenerator.csproj (ConfigurationSchemaGenerator) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using SourceGenerators; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { internal sealed class TypeIndex(IEnumerable<TypeSpec> typeSpecs) { private readonly Dictionary<TypeRef, TypeSpec> _index = typeSpecs.ToDictionary(spec => spec.TypeRef); public bool CanBindTo(TypeRef typeRef) => GetEffectiveTypeSpec(typeRef) switch { SimpleTypeSpec => true, ComplexTypeSpec complexTypeSpec => CanInstantiate(complexTypeSpec) || HasBindableMembers(complexTypeSpec), _ => throw new InvalidOperationException(), }; public bool CanInstantiate(ComplexTypeSpec typeSpec) => typeSpec switch { ObjectSpec objectSpec => objectSpec is { InstantiationStrategy: not ObjectInstantiationStrategy.None, InitExceptionMessage: null }, DictionarySpec dictionarySpec => KeyIsSupported(dictionarySpec), CollectionSpec collectionSpec => CanBindTo(collectionSpec.ElementTypeRef), _ => throw new InvalidOperationException(), }; public bool HasBindableMembers(ComplexTypeSpec typeSpec) => typeSpec switch { ObjectSpec objectSpec => objectSpec.Properties?.Any(ShouldBindTo) is true, DictionarySpec dictSpec => KeyIsSupported(dictSpec) && CanBindTo(dictSpec.ElementTypeRef), CollectionSpec collectionSpec => CanBindTo(collectionSpec.ElementTypeRef), _ => throw new InvalidOperationException(), }; public bool ShouldBindTo(PropertySpec property) { TypeSpec propTypeSpec = GetEffectiveTypeSpec(property.TypeRef); return IsAccessible() && !IsCollectionAndCannotOverride() && !IsDictWithUnsupportedKey(); bool IsAccessible() => property.CanGet || property.CanSet; bool IsDictWithUnsupportedKey() => propTypeSpec is DictionarySpec dictionarySpec && !KeyIsSupported(dictionarySpec); bool IsCollectionAndCannotOverride() => !property.CanSet && propTypeSpec is CollectionWithCtorInitSpec { InstantiationStrategy: CollectionInstantiationStrategy.CopyConstructor or CollectionInstantiationStrategy.LinqToDictionary }; } public TypeSpec GetEffectiveTypeSpec(TypeRef typeRef) { TypeSpec typeSpec = GetTypeSpec(typeRef); return GetEffectiveTypeSpec(typeSpec); } public TypeSpec GetEffectiveTypeSpec(TypeSpec typeSpec) { TypeRef effectiveRef = typeSpec.EffectiveTypeRef; TypeSpec effectiveSpec = effectiveRef == typeSpec.TypeRef ? typeSpec : _index[effectiveRef]; return effectiveSpec; } public TypeSpec GetTypeSpec(TypeRef typeRef) => _index[typeRef]; public static string GetInstantiationTypeDisplayString(CollectionWithCtorInitSpec type) { CollectionInstantiationConcreteType concreteType = type.InstantiationConcreteType; return concreteType is CollectionInstantiationConcreteType.Self ? type.TypeRef.FullyQualifiedName : GetGenericTypeDisplayString(type, concreteType); } public static string GetPopulationCastTypeDisplayString(CollectionWithCtorInitSpec type) { CollectionPopulationCastType castType = type.PopulationCastType; Debug.Assert(castType is not CollectionPopulationCastType.NotApplicable); return GetGenericTypeDisplayString(type, castType); } public static string GetGenericTypeDisplayString(CollectionWithCtorInitSpec type, Enum genericProxyTypeName) { string proxyTypeNameStr = genericProxyTypeName.ToString(); string elementTypeFQN = type.ElementTypeRef.FullyQualifiedName; if (type is EnumerableSpec) { return $"{proxyTypeNameStr}<{elementTypeFQN}>"; } string keyTypeDisplayString = ((DictionarySpec)type).KeyTypeRef.FullyQualifiedName; return $"{proxyTypeNameStr}<{keyTypeDisplayString}, {elementTypeFQN}>"; } public bool KeyIsSupported(DictionarySpec typeSpec) => // Only types that are parsable from string are supported. // Nullable keys not allowed; that would cause us to emit // code that violates dictionary key notnull constraint. GetTypeSpec(typeSpec.KeyTypeRef) is ParsableFromStringSpec; public static string GetConfigKeyCacheFieldName(ObjectSpec type) => $"s_configKeys_{type.IdentifierCompatibleSubstring}"; public static string GetParseMethodName(ParsableFromStringSpec type) { Debug.Assert(type.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue); if (type.StringParsableTypeKind is StringParsableTypeKind.ByteArray) { return "ParseByteArray"; } string displayString = type.TypeRef.FullyQualifiedName; const string GlobalPrefix = "global::"; if (displayString.StartsWith(GlobalPrefix)) { displayString = displayString.Substring(GlobalPrefix.Length); } Debug.Assert(displayString.Length > 0); if (char.IsLower(displayString[0])) { displayString = char.ToUpperInvariant(displayString[0]) + displayString.Substring(1); } if (displayString.Contains('.')) { displayString = displayString.Replace(".", ""); } return "Parse" + displayString; } } }