1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 using System.Collections.Generic;
6 using System.Reflection;
7 using System.Reflection.Context;
8 using System.Threading;
10 namespace System.ComponentModel.Composition.Registration
12 public class RegistrationBuilder : CustomReflectionContext
14 internal sealed class InnerRC : ReflectionContext
16 public override TypeInfo MapType(TypeInfo t) { return t; }
17 public override Assembly MapAssembly(Assembly a) { return a; }
20 private static readonly ReflectionContext s_inner = new InnerRC();
21 private static readonly List<object> s_emptyList = new List<object>();
23 private readonly Lock _lock = new Lock();
24 private readonly List<PartBuilder> _conventions = new List<PartBuilder>();
26 private readonly Dictionary<MemberInfo, List<Attribute>> _memberInfos = new Dictionary<MemberInfo, List<Attribute>>();
27 private readonly Dictionary<ParameterInfo, List<Attribute>> _parameters = new Dictionary<ParameterInfo, List<Attribute>>();
29 public RegistrationBuilder() : base(s_inner)
33 public PartBuilder<T> ForTypesDerivedFrom<T>()
35 var partBuilder = new PartBuilder<T>((t) => typeof(T) != t && typeof(T).IsAssignableFrom(t));
36 _conventions.Add(partBuilder);
41 public PartBuilder ForTypesDerivedFrom(Type type)
45 throw new ArgumentNullException(nameof(type));
48 var partBuilder = new PartBuilder((t) => type != t && type.IsAssignableFrom(t));
49 _conventions.Add(partBuilder);
54 public PartBuilder<T> ForType<T>()
56 var partBuilder = new PartBuilder<T>((t) => t == typeof(T));
57 _conventions.Add(partBuilder);
62 public PartBuilder ForType(Type type)
66 throw new ArgumentNullException(nameof(type));
69 var partBuilder = new PartBuilder((t) => t == type);
70 _conventions.Add(partBuilder);
75 public PartBuilder<T> ForTypesMatching<T>(Predicate<Type> typeFilter)
77 if (typeFilter is null)
79 throw new ArgumentNullException(nameof(typeFilter));
82 var partBuilder = new PartBuilder<T>(typeFilter);
83 _conventions.Add(partBuilder);
88 public PartBuilder ForTypesMatching(Predicate<Type> typeFilter)
90 if (typeFilter is null)
92 throw new ArgumentNullException(nameof(typeFilter));
95 var partBuilder = new PartBuilder(typeFilter);
96 _conventions.Add(partBuilder);
101 private IEnumerable<Tuple<object, List<Attribute>>> EvaluateThisTypeAgainstTheConvention(Type type)
103 List<Attribute> attributes = new List<Attribute>();
105 var configuredMembers = new List<Tuple<object, List<Attribute>>>();
106 bool specifiedConstructor = false;
107 bool matchedConvention = false;
109 foreach (PartBuilder builder in _conventions.Where(c => c.SelectType(type.UnderlyingSystemType)))
111 attributes.AddRange(builder.BuildTypeAttributes(type));
113 specifiedConstructor |= builder.BuildConstructorAttributes(type, ref configuredMembers);
114 builder.BuildPropertyAttributes(type, ref configuredMembers);
115 matchedConvention = true;
118 if (matchedConvention && !specifiedConstructor)
120 // DefaultConstructor
121 PartBuilder.BuildDefaultConstructorAttributes(type, ref configuredMembers);
124 configuredMembers.Add(Tuple.Create((object)type, attributes));
126 return configuredMembers;
129 // Handle Type Exports and Parts
130 protected override IEnumerable<object> GetCustomAttributes(System.Reflection.MemberInfo member, IEnumerable<object> declaredAttributes)
132 IEnumerable<object> attributes = base.GetCustomAttributes(member, declaredAttributes);
134 // Now edit the attributes returned from the base type
135 List<Attribute> cachedAttributes = null;
137 if (member.MemberType == MemberTypes.TypeInfo || member.MemberType == MemberTypes.NestedType)
139 MemberInfo underlyingMemberType = ((Type)member).UnderlyingSystemType;
140 using (new ReadLock(_lock))
142 _memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
145 if (cachedAttributes == null)
147 using (new WriteLock(_lock))
149 //Double check locking another thread may have inserted one while we were away.
150 if (!_memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes))
152 List<Attribute> attributeList;
153 foreach (Tuple<object, List<Attribute>> element in EvaluateThisTypeAgainstTheConvention((Type)member))
155 attributeList = element.Item2;
156 if (attributeList != null)
158 if (element.Item1 is MemberInfo)
160 List<Attribute> memberAttributes;
161 switch (((MemberInfo)element.Item1).MemberType)
163 case MemberTypes.Constructor:
164 if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
166 _memberInfos.Add((MemberInfo)element.Item1, element.Item2);
170 memberAttributes.AddRange(attributeList);
173 case MemberTypes.TypeInfo:
174 case MemberTypes.NestedType:
175 case MemberTypes.Property:
176 if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
178 _memberInfos.Add((MemberInfo)element.Item1, element.Item2);
182 memberAttributes.AddRange(attributeList);
191 if (!(element.Item1 is ParameterInfo))
192 throw new Exception(SR.Diagnostic_InternalExceptionMessage);
193 // Item contains as Constructor parameter to configure
194 if (!_parameters.TryGetValue((ParameterInfo)element.Item1, out List<Attribute> parameterAttributes))
196 _parameters.Add((ParameterInfo)element.Item1, element.Item2);
200 parameterAttributes.AddRange(cachedAttributes);
207 // We will have updated all of the MemberInfos by now so lets reload cachedAttributes wiuth the current store
208 _memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
212 else if (member.MemberType == System.Reflection.MemberTypes.Constructor || member.MemberType == System.Reflection.MemberTypes.Property)
214 cachedAttributes = ReadMemberCustomAttributes(member);
217 return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
220 //This is where ParameterImports will be handled
221 protected override IEnumerable<object> GetCustomAttributes(System.Reflection.ParameterInfo parameter, IEnumerable<object> declaredAttributes)
223 IEnumerable<object> attributes = base.GetCustomAttributes(parameter, declaredAttributes);
224 List<Attribute> cachedAttributes = ReadParameterCustomAttributes(parameter);
226 return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
229 private List<Attribute> ReadMemberCustomAttributes(MemberInfo member)
231 List<Attribute> cachedAttributes = null;
232 bool getMemberAttributes = false;
234 // Now edit the attributes returned from the base type
235 using (new ReadLock(_lock))
237 if (!_memberInfos.TryGetValue(member, out cachedAttributes))
239 // If there is nothing for this member Cache any attributes for the DeclaringType
240 if (!_memberInfos.TryGetValue(member.DeclaringType.UnderlyingSystemType, out cachedAttributes))
242 // If there is nothing for this parameter look to see if the declaring Member has been cached yet?
243 // need to do it outside of the lock, so set the flag we'll check it in a bit
244 getMemberAttributes = true;
247 cachedAttributes = null;
251 if (getMemberAttributes)
253 GetCustomAttributes(member.DeclaringType, s_emptyList);
255 // We should have run the rules for the enclosing parameter so we can again
256 using (new ReadLock(_lock))
258 _memberInfos.TryGetValue(member, out cachedAttributes);
262 return cachedAttributes;
265 private List<Attribute> ReadParameterCustomAttributes(ParameterInfo parameter)
267 List<Attribute> cachedAttributes = null;
268 bool getMemberAttributes = false;
270 // Now edit the attributes returned from the base type
271 using (new ReadLock(_lock))
273 if (!_parameters.TryGetValue(parameter, out cachedAttributes))
275 // If there is nothing for this parameter Cache any attributes for the DeclaringType
276 if (!_memberInfos.TryGetValue(parameter.Member.DeclaringType, out cachedAttributes))
278 // If there is nothing for this parameter look to see if the declaring Member has been cached yet?
279 // need to do it outside of the lock, so set the flag we'll check it in a bit
280 getMemberAttributes = true;
282 cachedAttributes = null;
286 if (getMemberAttributes)
288 GetCustomAttributes(parameter.Member.DeclaringType, s_emptyList);
290 // We should have run the rules for the enclosing parameter so we can again
291 using (new ReadLock(_lock))
293 _parameters.TryGetValue(parameter, out cachedAttributes);
297 return cachedAttributes;