//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities.Presentation.Internal.Metadata { using System.Activities.Presentation.Internal.Properties; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Windows; using System.Activities.Presentation.Metadata; using System.Runtime; using System.Diagnostics.CodeAnalysis; using System.Activities.Presentation; // // This class is used by the attribute table builder to // add attributes. It is then handed to AttributeTable // and accessed in a read-only fashion. // internal class MutableAttributeTable { private static object[] _empty = new object[0]; private Dictionary _metadata; internal MutableAttributeTable() { _metadata = new Dictionary(); } // // Returns the types we're handing metadata for // internal IEnumerable AttributedTypes { get { return _metadata.Keys; } } // // Private helper to add a portion of an existing table. // private static void AddAttributeMetadata(TypeMetadata newMd, TypeMetadata existingMd) { if (newMd.TypeAttributes != null) { if (existingMd.TypeAttributes != null) { existingMd.TypeAttributes.AddRange(newMd.TypeAttributes); } else { existingMd.TypeAttributes = newMd.TypeAttributes; } } } // // Helper to add a enum of attributes ot an existing list // private static void AddAttributes(AttributeList list, IEnumerable attributes) { // Attributes are ordered so those at the end of the // list take prececence over those at the front. list.AddRange(attributes); } internal void AddCallback(Type type, AttributeCallback callback) { Fx.Assert(type != null && callback != null, "type or callback parameter is null"); AttributeList list = GetTypeList(type); list.Add(callback); } // // Adds custom attrs for a type // internal void AddCustomAttributes(Type type, IEnumerable attributes) { Fx.Assert(type != null && attributes != null, "type or attributes parameter is null"); AddAttributes(GetTypeList(type), attributes); } // // Adds custom attrs for a descriptor // internal void AddCustomAttributes(Type ownerType, MemberDescriptor descriptor, IEnumerable attributes) { Fx.Assert(ownerType != null && descriptor != null && attributes != null, "ownerType/descriptor/attributes is null"); AddAttributes(GetMemberList(ownerType, descriptor.Name), attributes); } // // Adds custom attrs for a member // internal void AddCustomAttributes(Type ownerType, MemberInfo member, IEnumerable attributes) { Fx.Assert(ownerType != null && member != null && attributes != null, "ownertype/member/attributes parameter is null"); AddAttributes(GetMemberList(ownerType, member.Name), attributes); } // // Adds custom attrs for a dp // internal void AddCustomAttributes(Type ownerType, DependencyProperty dp, IEnumerable attributes) { Fx.Assert(ownerType != null && dp != null && attributes != null, "ownerType/dp/attributes parameter is null"); AddAttributes(GetMemberList(ownerType, dp.Name), attributes); } // // Adds custom attrs for a member name // internal void AddCustomAttributes(Type ownerType, string memberName, IEnumerable attributes) { Fx.Assert(ownerType != null && memberName != null && attributes != null, "ownerType/membername/attributes parameter is null"); AddAttributes(GetMemberList(ownerType, memberName), attributes); } // // Private helper to add a portion of an existing table. // private static void AddMemberMetadata(TypeMetadata newMd, TypeMetadata existingMd) { if (newMd.MemberAttributes != null) { if (existingMd.MemberAttributes != null) { foreach (KeyValuePair kv in newMd.MemberAttributes) { AttributeList existing; if (existingMd.MemberAttributes.TryGetValue(kv.Key, out existing)) { existing.AddRange(kv.Value); } else { existingMd.MemberAttributes.Add(kv.Key, kv.Value); } } } else { existingMd.MemberAttributes = newMd.MemberAttributes; } } } // // Adds an existing table. // internal void AddTable(MutableAttributeTable table) { Fx.Assert(table != null, "table parameter is null"); foreach (KeyValuePair kv in table._metadata) { AddTypeMetadata(kv.Key, kv.Value); } } // // Private helper to add a portion of an existing table. // private void AddTypeMetadata(Type type, TypeMetadata md) { TypeMetadata existing; if (_metadata.TryGetValue(type, out existing)) { AddAttributeMetadata(md, existing); AddMemberMetadata(md, existing); } else { _metadata.Add(type, md); } } // // Returns true if this table contains attributes for the // given type // internal bool ContainsAttributes(Type type) { Fx.Assert(type != null, "type parameter is null"); return (_metadata.ContainsKey(type)); } // // Helper method that walks through an attribute list and expands all callbacks // within it. // private void ExpandAttributes(Type type, AttributeList attributes) { Fx.Assert(!attributes.IsExpanded, "Should not call expand attributes with an expanded list."); // First, expand all the callbacks. This may add more attributes // into our list // for (int idx = 0; idx < attributes.Count; idx++) { AttributeCallback callback = attributes[idx] as AttributeCallback; while (callback != null) { attributes.RemoveAt(idx); AttributeCallbackBuilder builder = new AttributeCallbackBuilder(this, type); callback(builder); if (idx < attributes.Count) { callback = attributes[idx] as AttributeCallback; } else { callback = null; } } } } // // Returns custom attributes for the type. // internal IEnumerable GetCustomAttributes(Type type) { Fx.Assert(type != null, "type parameter is null"); AttributeList attributes = GetExpandedAttributes(type, null, delegate(Type typeToGet, object callbackParam) { TypeMetadata md; if (_metadata.TryGetValue(typeToGet, out md)) { return md.TypeAttributes; } return null; }); if (attributes != null) { return attributes.AsReadOnly(); } return _empty; } // // Returns custom attributes for the descriptor. // internal IEnumerable GetCustomAttributes(Type ownerType, MemberDescriptor descriptor) { Fx.Assert(ownerType != null && descriptor != null, "ownerType or descriptor parameter is null"); return GetCustomAttributes(ownerType, descriptor.Name); } // // Returns custom attributes for the dp. // internal IEnumerable GetCustomAttributes(Type ownerType, DependencyProperty dp) { Fx.Assert(ownerType != null && dp != null, "ownerType or dp parameter is null"); return GetCustomAttributes(ownerType, dp.Name); } // // Returns custom attributes for the member. // internal IEnumerable GetCustomAttributes(Type ownerType, MemberInfo member) { Fx.Assert(ownerType != null && member != null, "ownerType or memeber parameter is null"); return GetCustomAttributes(ownerType, member.Name); } // // Returns custom attributes for the member. // internal IEnumerable GetCustomAttributes(Type ownerType, string memberName) { Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null"); AttributeList attributes = GetExpandedAttributes(ownerType, memberName, delegate(Type typeToGet, object callbackParam) { string name = (string)callbackParam; TypeMetadata md; if (_metadata.TryGetValue(typeToGet, out md)) { // If member attributes are null but type attributes are not, // it is possible that expanding type attributes could cause // member attributes to be added. Check. if (md.MemberAttributes == null && md.TypeAttributes != null && !md.TypeAttributes.IsExpanded) { ExpandAttributes(ownerType, md.TypeAttributes); } if (md.MemberAttributes != null) { AttributeList list; if (md.MemberAttributes.TryGetValue(name, out list)) { return list; } } } return null; }); if (attributes != null) { return attributes.AsReadOnly(); } return _empty; } // // Helper to demand create the attribute list ofr a dependency property. // private AttributeList GetMemberList(Type ownerType, string memberName) { Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null"); TypeMetadata md = GetTypeMetadata(ownerType); if (md.MemberAttributes == null) { md.MemberAttributes = new Dictionary(); } AttributeList list; if (!md.MemberAttributes.TryGetValue(memberName, out list)) { list = new AttributeList(); md.MemberAttributes.Add(memberName, list); } return list; } // // Expands a type attribute table for use. // Attribute tables only contain attributes for // the given type, and may have callbacks embedded // within them. // private AttributeList GetExpandedAttributes(Type type, object callbackParam, GetAttributesCallback callback) { // Do we have attributes to expand? AttributeList attributes = callback(type, callbackParam); if (attributes != null) { // If these attributes haven't been expanded yet, do that // now. if (!attributes.IsExpanded) { // We have a lock here because multiple people could be // surfing type information at the same time from multiple // threads. While we are read only once we are expanded, // we do modify the list here to expand the callbacks and // merge. Therefore, we need to acquire a lock. lock (attributes) { if (!attributes.IsExpanded) { ExpandAttributes(type, attributes); attributes.IsExpanded = true; } } } } return attributes; } // // Helper to demand create the attribute list for a type. // private AttributeList GetTypeList(Type type) { Fx.Assert(type != null, "type parameter is null"); TypeMetadata md = GetTypeMetadata(type); if (md.TypeAttributes == null) { md.TypeAttributes = new AttributeList(); } return md.TypeAttributes; } // // Helper to demand create the type metadata. // private TypeMetadata GetTypeMetadata(Type type) { Fx.Assert(type != null, "type parameter is null"); TypeMetadata md; if (!_metadata.TryGetValue(type, out md)) { md = new TypeMetadata(); _metadata.Add(type, md); } return md; } // // Called by the MetadataStore to walk through all the metadata and // ensure that it can be found on the appropriate types and members. // Any asserts that come from here are bugs in the type description // provider. // internal void DebugValidateProvider() { #if DEBUG foreach (KeyValuePair kv in _metadata ) { if (kv.Value.TypeAttributes != null) { AttributeCollection attrs = TypeDescriptor.GetAttributes(kv.Key); foreach (object o in kv.Value .TypeAttributes) { Attribute a = o as Attribute; if (a != null) { bool found = false; foreach (Attribute a2 in attrs) { if (a.TypeId.Equals (a2.TypeId)) { found = true; break; } } Fx.Assert( found, string.Format(CultureInfo.CurrentCulture, "Attribute {0} on type {1} is missing from provider.", a.GetType().Name, kv.Key.Name)); } } } if (kv.Value .MemberAttributes != null) { foreach (KeyValuePair kvDesc in kv.Value.MemberAttributes) { PropertyDescriptor p; EventDescriptor e; AttributeCollection attrs = null; string member = "unknown"; if ((p = TypeDescriptor.GetProperties(kv.Key)[kvDesc.Key]) != null) { attrs = p.Attributes; member = p.Name; } else if ((e = TypeDescriptor.GetEvents(kv.Key)[kvDesc.Key]) != null) { attrs = e.Attributes; member = e.Name; } else if ((p = DependencyPropertyDescriptor.FromName(kvDesc.Key, kv.Key, typeof( DependencyObject))) != null) { attrs = p.Attributes; member = p.Name; } if (attrs != null) { foreach (object o in kvDesc.Value) { Attribute a = o as Attribute; if (a != null) { bool found = false; foreach (Attribute a2 in attrs) { if (a.TypeId.Equals(a2.TypeId)) { found = true; break; } } Fx.Assert( found, string.Format(CultureInfo.CurrentCulture, "Attribute {0} on member {1}.{2} is missing from provider.", a.GetType().Name, kv.Key.Name, member)); } } } } } } #else #endif } // // Performs validation of all metadata in the table. // This expands all callbacks so it can be very expensive // for large tables. // public void ValidateTable() { List errors = null; foreach (KeyValuePair kv in _metadata) { // Walk type attributes. We don't need to compare these // to anything because there is no way for them to be // invalid. We simply get them to ensure they don't throw GetCustomAttributes(kv.Key); // Walk member attributes. We need to ensure that all member descriptors // of type LookupMemberDescriptor have matching members (property or event) // on the target type. Other members are already validated by the fact // that they exist. if (kv.Value.MemberAttributes != null) { foreach (KeyValuePair kvMember in kv.Value.MemberAttributes) { // Validate that the attribute expansion doesn't throw GetCustomAttributes(kv.Key, kvMember.Key); // Validate that the member name matches a real proeprty/event and there // are no duplicates PropertyDescriptor p = DependencyPropertyDescriptor.FromName(kvMember.Key, kv.Key, typeof(DependencyObject)); EventDescriptor e = TypeDescriptor.GetEvents(kv.Key)[kvMember.Key]; if (p == null && e == null) { p = TypeDescriptor.GetProperties(kv.Key)[kvMember.Key]; } string errorMsg = null; if (p == null && e == null) { errorMsg = string.Format( CultureInfo.CurrentCulture, Resources.Error_ValidationNoMatchingMember, kvMember.Key, kv.Key.FullName); } else if (p != null && e != null) { errorMsg = string.Format( CultureInfo.CurrentCulture, Resources.Error_ValidationAmbiguousMember, kvMember.Key, kv.Key.FullName); } if (errorMsg != null) { if (errors == null) { errors = new List(); } errors.Add(errorMsg); } } } } // Did we get any errors? if (errors != null) { throw FxTrace.Exception.AsError(new AttributeTableValidationException( Resources.Error_TableValidationFailed, errors)); } } // // We have a generic attribute expansion routine // that relies on someone else providing a mechanism // for returning the base attribute list. If there // is no base list, this callback can return null. // private delegate AttributeList GetAttributesCallback(Type type, object callbackParam); // // All metadata for a type is stored here. // private class TypeMetadata { internal AttributeList TypeAttributes; internal Dictionary MemberAttributes; } // // Individual attributes for a member or type are stored // here. Attribute lists can be "expanded", so their // callbacks are evaluated and their attributes are // merged with their base attribute list. // private class AttributeList : List { private bool _isExpanded; internal bool IsExpanded { get { return _isExpanded; } set { _isExpanded = value; } } } } }