1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Activities.Presentation.Internal.Metadata
8 using System.Activities.Presentation.Internal.Properties;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.ComponentModel;
13 using System.Diagnostics;
14 using System.Globalization;
15 using System.Reflection;
17 using System.Activities.Presentation.Metadata;
19 using System.Diagnostics.CodeAnalysis;
20 using System.Activities.Presentation;
23 // This class is used by the attribute table builder to
24 // add attributes. It is then handed to AttributeTable
25 // and accessed in a read-only fashion.
27 internal class MutableAttributeTable
29 private static object[] _empty = new object[0];
31 private Dictionary<Type, TypeMetadata> _metadata;
33 internal MutableAttributeTable()
35 _metadata = new Dictionary<Type, TypeMetadata>();
39 // Returns the types we're handing metadata for
41 internal IEnumerable<Type> AttributedTypes
43 get { return _metadata.Keys; }
47 // Private helper to add a portion of an existing table.
49 private static void AddAttributeMetadata(TypeMetadata newMd, TypeMetadata existingMd)
51 if (newMd.TypeAttributes != null)
53 if (existingMd.TypeAttributes != null)
55 existingMd.TypeAttributes.AddRange(newMd.TypeAttributes);
59 existingMd.TypeAttributes = newMd.TypeAttributes;
65 // Helper to add a enum of attributes ot an existing list
67 private static void AddAttributes(AttributeList list, IEnumerable<object> attributes)
69 // Attributes are ordered so those at the end of the
70 // list take prececence over those at the front.
71 list.AddRange(attributes);
74 internal void AddCallback(Type type, AttributeCallback callback)
76 Fx.Assert(type != null && callback != null, "type or callback parameter is null");
77 AttributeList list = GetTypeList(type);
82 // Adds custom attrs for a type
84 internal void AddCustomAttributes(Type type, IEnumerable<object> attributes)
86 Fx.Assert(type != null && attributes != null, "type or attributes parameter is null");
87 AddAttributes(GetTypeList(type), attributes);
91 // Adds custom attrs for a descriptor
93 internal void AddCustomAttributes(Type ownerType, MemberDescriptor descriptor, IEnumerable<object> attributes)
95 Fx.Assert(ownerType != null && descriptor != null && attributes != null, "ownerType/descriptor/attributes is null");
96 AddAttributes(GetMemberList(ownerType, descriptor.Name), attributes);
100 // Adds custom attrs for a member
102 internal void AddCustomAttributes(Type ownerType, MemberInfo member, IEnumerable<object> attributes)
104 Fx.Assert(ownerType != null && member != null && attributes != null, "ownertype/member/attributes parameter is null");
105 AddAttributes(GetMemberList(ownerType, member.Name), attributes);
109 // Adds custom attrs for a dp
111 internal void AddCustomAttributes(Type ownerType, DependencyProperty dp, IEnumerable<object> attributes)
113 Fx.Assert(ownerType != null && dp != null && attributes != null, "ownerType/dp/attributes parameter is null");
114 AddAttributes(GetMemberList(ownerType, dp.Name), attributes);
118 // Adds custom attrs for a member name
120 internal void AddCustomAttributes(Type ownerType, string memberName, IEnumerable<object> attributes)
122 Fx.Assert(ownerType != null && memberName != null && attributes != null, "ownerType/membername/attributes parameter is null");
123 AddAttributes(GetMemberList(ownerType, memberName), attributes);
127 // Private helper to add a portion of an existing table.
129 private static void AddMemberMetadata(TypeMetadata newMd, TypeMetadata existingMd)
131 if (newMd.MemberAttributes != null)
133 if (existingMd.MemberAttributes != null)
135 foreach (KeyValuePair<string, AttributeList> kv in newMd.MemberAttributes)
137 AttributeList existing;
138 if (existingMd.MemberAttributes.TryGetValue(kv.Key, out existing))
140 existing.AddRange(kv.Value);
144 existingMd.MemberAttributes.Add(kv.Key, kv.Value);
150 existingMd.MemberAttributes = newMd.MemberAttributes;
156 // Adds an existing table.
158 internal void AddTable(MutableAttributeTable table)
160 Fx.Assert(table != null, "table parameter is null");
161 foreach (KeyValuePair<Type, TypeMetadata> kv in table._metadata)
163 AddTypeMetadata(kv.Key, kv.Value);
168 // Private helper to add a portion of an existing table.
170 private void AddTypeMetadata(Type type, TypeMetadata md)
172 TypeMetadata existing;
173 if (_metadata.TryGetValue(type, out existing))
175 AddAttributeMetadata(md, existing);
176 AddMemberMetadata(md, existing);
180 _metadata.Add(type, md);
185 // Returns true if this table contains attributes for the
188 internal bool ContainsAttributes(Type type)
190 Fx.Assert(type != null, "type parameter is null");
191 return (_metadata.ContainsKey(type));
195 // Helper method that walks through an attribute list and expands all callbacks
198 private void ExpandAttributes(Type type, AttributeList attributes)
200 Fx.Assert(!attributes.IsExpanded, "Should not call expand attributes with an expanded list.");
202 // First, expand all the callbacks. This may add more attributes
205 for (int idx = 0; idx < attributes.Count; idx++)
207 AttributeCallback callback = attributes[idx] as AttributeCallback;
208 while (callback != null)
210 attributes.RemoveAt(idx);
211 AttributeCallbackBuilder builder = new AttributeCallbackBuilder(this, type);
214 if (idx < attributes.Count)
216 callback = attributes[idx] as AttributeCallback;
227 // Returns custom attributes for the type.
229 internal IEnumerable GetCustomAttributes(Type type)
231 Fx.Assert(type != null, "type parameter is null");
233 AttributeList attributes = GetExpandedAttributes(type, null, delegate(Type typeToGet, object callbackParam)
236 if (_metadata.TryGetValue(typeToGet, out md))
238 return md.TypeAttributes;
243 if (attributes != null)
245 return attributes.AsReadOnly();
252 // Returns custom attributes for the descriptor.
254 internal IEnumerable GetCustomAttributes(Type ownerType, MemberDescriptor descriptor)
256 Fx.Assert(ownerType != null && descriptor != null, "ownerType or descriptor parameter is null");
257 return GetCustomAttributes(ownerType, descriptor.Name);
261 // Returns custom attributes for the dp.
263 internal IEnumerable GetCustomAttributes(Type ownerType, DependencyProperty dp)
265 Fx.Assert(ownerType != null && dp != null, "ownerType or dp parameter is null");
266 return GetCustomAttributes(ownerType, dp.Name);
270 // Returns custom attributes for the member.
272 internal IEnumerable GetCustomAttributes(Type ownerType, MemberInfo member)
274 Fx.Assert(ownerType != null && member != null, "ownerType or memeber parameter is null");
275 return GetCustomAttributes(ownerType, member.Name);
279 // Returns custom attributes for the member.
281 internal IEnumerable GetCustomAttributes(Type ownerType, string memberName)
283 Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null");
285 AttributeList attributes = GetExpandedAttributes(ownerType, memberName, delegate(Type typeToGet, object callbackParam)
287 string name = (string)callbackParam;
290 if (_metadata.TryGetValue(typeToGet, out md))
293 // If member attributes are null but type attributes are not,
294 // it is possible that expanding type attributes could cause
295 // member attributes to be added. Check.
297 if (md.MemberAttributes == null && md.TypeAttributes != null && !md.TypeAttributes.IsExpanded)
299 ExpandAttributes(ownerType, md.TypeAttributes);
302 if (md.MemberAttributes != null)
305 if (md.MemberAttributes.TryGetValue(name, out list))
315 if (attributes != null)
317 return attributes.AsReadOnly();
324 // Helper to demand create the attribute list ofr a dependency property.
326 private AttributeList GetMemberList(Type ownerType, string memberName)
328 Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null");
329 TypeMetadata md = GetTypeMetadata(ownerType);
331 if (md.MemberAttributes == null)
333 md.MemberAttributes = new Dictionary<string, AttributeList>();
337 if (!md.MemberAttributes.TryGetValue(memberName, out list))
339 list = new AttributeList();
340 md.MemberAttributes.Add(memberName, list);
347 // Expands a type attribute table for use.
348 // Attribute tables only contain attributes for
349 // the given type, and may have callbacks embedded
352 private AttributeList GetExpandedAttributes(Type type, object callbackParam, GetAttributesCallback callback)
355 // Do we have attributes to expand?
357 AttributeList attributes = callback(type, callbackParam);
358 if (attributes != null)
361 // If these attributes haven't been expanded yet, do that
364 if (!attributes.IsExpanded)
367 // We have a lock here because multiple people could be
368 // surfing type information at the same time from multiple
369 // threads. While we are read only once we are expanded,
370 // we do modify the list here to expand the callbacks and
371 // merge. Therefore, we need to acquire a lock.
375 if (!attributes.IsExpanded)
377 ExpandAttributes(type, attributes);
378 attributes.IsExpanded = true;
388 // Helper to demand create the attribute list for a type.
390 private AttributeList GetTypeList(Type type)
392 Fx.Assert(type != null, "type parameter is null");
393 TypeMetadata md = GetTypeMetadata(type);
394 if (md.TypeAttributes == null)
396 md.TypeAttributes = new AttributeList();
398 return md.TypeAttributes;
402 // Helper to demand create the type metadata.
404 private TypeMetadata GetTypeMetadata(Type type)
406 Fx.Assert(type != null, "type parameter is null");
408 if (!_metadata.TryGetValue(type, out md))
410 md = new TypeMetadata();
411 _metadata.Add(type, md);
418 // Called by the MetadataStore to walk through all the metadata and
419 // ensure that it can be found on the appropriate types and members.
420 // Any asserts that come from here are bugs in the type description
423 internal void DebugValidateProvider()
426 foreach (KeyValuePair<Type, TypeMetadata> kv in _metadata
428 if (kv.Value.TypeAttributes !=
430 AttributeCollection attrs = TypeDescriptor.GetAttributes(kv.Key);
431 foreach (object o in kv.Value
433 Attribute a = o as Attribute;
437 foreach (Attribute a2
448 string.Format(CultureInfo.CurrentCulture, "Attribute {0} on type {1} is missing from provider.",
449 a.GetType().Name, kv.Key.Name));
455 .MemberAttributes != null) {
456 foreach (KeyValuePair<string, AttributeList> kvDesc
457 in kv.Value.MemberAttributes) {
458 PropertyDescriptor p;
460 AttributeCollection attrs = null;
461 string member = "unknown";
463 if ((p = TypeDescriptor.GetProperties(kv.Key)[kvDesc.Key]) != null) {
464 attrs = p.Attributes;
467 else if ((e = TypeDescriptor.GetEvents(kv.Key)[kvDesc.Key]) != null) {
468 attrs = e.Attributes;
471 else if ((p = DependencyPropertyDescriptor.FromName(kvDesc.Key, kv.Key, typeof(
472 DependencyObject))) != null) {
473 attrs = p.Attributes;
479 (object o in kvDesc.Value) {
480 Attribute a = o as Attribute;
485 foreach (Attribute a2 in attrs) {
487 if (a.TypeId.Equals(a2.TypeId)) {
495 string.Format(CultureInfo.CurrentCulture, "Attribute {0} on member {1}.{2} is missing from provider.",
496 a.GetType().Name, kv.Key.Name, member));
508 // Performs validation of all metadata in the table.
509 // This expands all callbacks so it can be very expensive
512 public void ValidateTable()
515 List<string> errors = null;
517 foreach (KeyValuePair<Type, TypeMetadata> kv in _metadata)
520 // Walk type attributes. We don't need to compare these
521 // to anything because there is no way for them to be
522 // invalid. We simply get them to ensure they don't throw
524 GetCustomAttributes(kv.Key);
526 // Walk member attributes. We need to ensure that all member descriptors
527 // of type LookupMemberDescriptor have matching members (property or event)
528 // on the target type. Other members are already validated by the fact
531 if (kv.Value.MemberAttributes != null)
534 foreach (KeyValuePair<string, AttributeList> kvMember in kv.Value.MemberAttributes)
537 // Validate that the attribute expansion doesn't throw
538 GetCustomAttributes(kv.Key, kvMember.Key);
540 // Validate that the member name matches a real proeprty/event and there
543 PropertyDescriptor p = DependencyPropertyDescriptor.FromName(kvMember.Key, kv.Key, typeof(DependencyObject));
544 EventDescriptor e = TypeDescriptor.GetEvents(kv.Key)[kvMember.Key];
545 if (p == null && e == null)
547 p = TypeDescriptor.GetProperties(kv.Key)[kvMember.Key];
550 string errorMsg = null;
551 if (p == null && e == null)
553 errorMsg = string.Format(
554 CultureInfo.CurrentCulture,
555 Resources.Error_ValidationNoMatchingMember,
556 kvMember.Key, kv.Key.FullName);
558 else if (p != null && e != null)
560 errorMsg = string.Format(
561 CultureInfo.CurrentCulture,
562 Resources.Error_ValidationAmbiguousMember,
563 kvMember.Key, kv.Key.FullName);
566 if (errorMsg != null)
570 errors = new List<string>();
572 errors.Add(errorMsg);
578 // Did we get any errors?
581 throw FxTrace.Exception.AsError(new AttributeTableValidationException(
582 Resources.Error_TableValidationFailed,
588 // We have a generic attribute expansion routine
589 // that relies on someone else providing a mechanism
590 // for returning the base attribute list. If there
591 // is no base list, this callback can return null.
593 private delegate AttributeList GetAttributesCallback(Type type, object callbackParam);
596 // All metadata for a type is stored here.
598 private class TypeMetadata
600 internal AttributeList TypeAttributes;
601 internal Dictionary<string, AttributeList> MemberAttributes;
605 // Individual attributes for a member or type are stored
606 // here. Attribute lists can be "expanded", so their
607 // callbacks are evaluated and their attributes are
608 // merged with their base attribute list.
610 private class AttributeList : List<object>
612 private bool _isExpanded;
614 internal bool IsExpanded
616 get { return _isExpanded; }
617 set { _isExpanded = value; }