|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Context;
using System.Threading;
namespace System.ComponentModel.Composition.Registration
{
public class RegistrationBuilder : CustomReflectionContext
{
internal sealed class InnerRC : ReflectionContext
{
public override TypeInfo MapType(TypeInfo t) { return t; }
public override Assembly MapAssembly(Assembly a) { return a; }
}
private static readonly ReflectionContext s_inner = new InnerRC();
private static readonly List<object> s_emptyList = new List<object>();
private readonly ReadWriteLock _lock = new ReadWriteLock();
private readonly List<PartBuilder> _conventions = new List<PartBuilder>();
private readonly Dictionary<MemberInfo, List<Attribute>> _memberInfos = new Dictionary<MemberInfo, List<Attribute>>();
private readonly Dictionary<ParameterInfo, List<Attribute>> _parameters = new Dictionary<ParameterInfo, List<Attribute>>();
public RegistrationBuilder() : base(s_inner)
{
}
public PartBuilder<T> ForTypesDerivedFrom<T>()
{
var partBuilder = new PartBuilder<T>((t) => typeof(T) != t && typeof(T).IsAssignableFrom(t));
_conventions.Add(partBuilder);
return partBuilder;
}
public PartBuilder ForTypesDerivedFrom(Type type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
var partBuilder = new PartBuilder((t) => type != t && type.IsAssignableFrom(t));
_conventions.Add(partBuilder);
return partBuilder;
}
public PartBuilder<T> ForType<T>()
{
var partBuilder = new PartBuilder<T>((t) => t == typeof(T));
_conventions.Add(partBuilder);
return partBuilder;
}
public PartBuilder ForType(Type type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
var partBuilder = new PartBuilder((t) => t == type);
_conventions.Add(partBuilder);
return partBuilder;
}
public PartBuilder<T> ForTypesMatching<T>(Predicate<Type> typeFilter)
{
if (typeFilter is null)
{
throw new ArgumentNullException(nameof(typeFilter));
}
var partBuilder = new PartBuilder<T>(typeFilter);
_conventions.Add(partBuilder);
return partBuilder;
}
public PartBuilder ForTypesMatching(Predicate<Type> typeFilter)
{
if (typeFilter is null)
{
throw new ArgumentNullException(nameof(typeFilter));
}
var partBuilder = new PartBuilder(typeFilter);
_conventions.Add(partBuilder);
return partBuilder;
}
private List<Tuple<object, List<Attribute>>> EvaluateThisTypeAgainstTheConvention(Type type)
{
List<Attribute> attributes = new List<Attribute>();
var configuredMembers = new List<Tuple<object, List<Attribute>>>();
bool specifiedConstructor = false;
bool matchedConvention = false;
foreach (PartBuilder builder in _conventions.Where(c => c.SelectType(type.UnderlyingSystemType)))
{
attributes.AddRange(builder.BuildTypeAttributes(type));
specifiedConstructor |= builder.BuildConstructorAttributes(type, ref configuredMembers);
builder.BuildPropertyAttributes(type, ref configuredMembers);
matchedConvention = true;
}
if (matchedConvention && !specifiedConstructor)
{
// DefaultConstructor
PartBuilder.BuildDefaultConstructorAttributes(type, ref configuredMembers);
}
configuredMembers.Add(Tuple.Create((object)type, attributes));
return configuredMembers;
}
// Handle Type Exports and Parts
protected override IEnumerable<object> GetCustomAttributes(System.Reflection.MemberInfo member, IEnumerable<object> declaredAttributes)
{
IEnumerable<object> attributes = base.GetCustomAttributes(member, declaredAttributes);
// Now edit the attributes returned from the base type
List<Attribute> cachedAttributes = null;
if (member.MemberType == MemberTypes.TypeInfo || member.MemberType == MemberTypes.NestedType)
{
MemberInfo underlyingMemberType = ((Type)member).UnderlyingSystemType;
using (new ReadLock(_lock))
{
_memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
}
if (cachedAttributes == null)
{
using (new WriteLock(_lock))
{
//Double check locking another thread may have inserted one while we were away.
if (!_memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes))
{
List<Attribute> attributeList;
foreach (Tuple<object, List<Attribute>> element in EvaluateThisTypeAgainstTheConvention((Type)member))
{
attributeList = element.Item2;
if (attributeList != null)
{
if (element.Item1 is MemberInfo)
{
List<Attribute> memberAttributes;
switch (((MemberInfo)element.Item1).MemberType)
{
case MemberTypes.Constructor:
if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
{
_memberInfos.Add((MemberInfo)element.Item1, element.Item2);
}
else
{
memberAttributes.AddRange(attributeList);
}
break;
case MemberTypes.TypeInfo:
case MemberTypes.NestedType:
case MemberTypes.Property:
if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
{
_memberInfos.Add((MemberInfo)element.Item1, element.Item2);
}
else
{
memberAttributes.AddRange(attributeList);
}
break;
default:
break;
}
}
else
{
if (!(element.Item1 is ParameterInfo))
throw new Exception(SR.Diagnostic_InternalExceptionMessage);
// Item contains as Constructor parameter to configure
if (!_parameters.TryGetValue((ParameterInfo)element.Item1, out List<Attribute> parameterAttributes))
{
_parameters.Add((ParameterInfo)element.Item1, element.Item2);
}
else
{
parameterAttributes.AddRange(cachedAttributes);
}
}
}
}
}
// We will have updated all of the MemberInfos by now so lets reload cachedAttributes wiuth the current store
_memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
}
}
}
else if (member.MemberType == System.Reflection.MemberTypes.Constructor || member.MemberType == System.Reflection.MemberTypes.Property)
{
cachedAttributes = ReadMemberCustomAttributes(member);
}
return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
}
//This is where ParameterImports will be handled
protected override IEnumerable<object> GetCustomAttributes(System.Reflection.ParameterInfo parameter, IEnumerable<object> declaredAttributes)
{
IEnumerable<object> attributes = base.GetCustomAttributes(parameter, declaredAttributes);
List<Attribute> cachedAttributes = ReadParameterCustomAttributes(parameter);
return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
}
private List<Attribute> ReadMemberCustomAttributes(MemberInfo member)
{
List<Attribute> cachedAttributes = null;
bool getMemberAttributes = false;
// Now edit the attributes returned from the base type
using (new ReadLock(_lock))
{
if (!_memberInfos.TryGetValue(member, out cachedAttributes))
{
// If there is nothing for this member Cache any attributes for the DeclaringType
if (!_memberInfos.TryGetValue(member.DeclaringType.UnderlyingSystemType, out cachedAttributes))
{
// If there is nothing for this parameter look to see if the declaring Member has been cached yet?
// need to do it outside of the lock, so set the flag we'll check it in a bit
getMemberAttributes = true;
}
cachedAttributes = null;
}
}
if (getMemberAttributes)
{
GetCustomAttributes(member.DeclaringType, s_emptyList);
// We should have run the rules for the enclosing parameter so we can again
using (new ReadLock(_lock))
{
_memberInfos.TryGetValue(member, out cachedAttributes);
}
}
return cachedAttributes;
}
private List<Attribute> ReadParameterCustomAttributes(ParameterInfo parameter)
{
List<Attribute> cachedAttributes = null;
bool getMemberAttributes = false;
// Now edit the attributes returned from the base type
using (new ReadLock(_lock))
{
if (!_parameters.TryGetValue(parameter, out cachedAttributes))
{
// If there is nothing for this parameter Cache any attributes for the DeclaringType
if (!_memberInfos.TryGetValue(parameter.Member.DeclaringType, out cachedAttributes))
{
// If there is nothing for this parameter look to see if the declaring Member has been cached yet?
// need to do it outside of the lock, so set the flag we'll check it in a bit
getMemberAttributes = true;
}
cachedAttributes = null;
}
}
if (getMemberAttributes)
{
GetCustomAttributes(parameter.Member.DeclaringType, s_emptyList);
// We should have run the rules for the enclosing parameter so we can again
using (new ReadLock(_lock))
{
_parameters.TryGetValue(parameter, out cachedAttributes);
}
}
return cachedAttributes;
}
}
}
|