[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / Metadata / MutableAttributeTable.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Internal.Metadata 
6 {
7
8     using System.Activities.Presentation.Internal.Properties;
9     using System;
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;
16     using System.Windows;
17     using System.Activities.Presentation.Metadata;
18     using System.Runtime;
19     using System.Diagnostics.CodeAnalysis;
20     using System.Activities.Presentation;
21
22     //
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.
26     //
27     internal class MutableAttributeTable 
28     {
29         private static object[] _empty = new object[0];
30
31         private Dictionary<Type, TypeMetadata> _metadata;
32
33         internal MutableAttributeTable() 
34         {
35             _metadata = new Dictionary<Type, TypeMetadata>();
36         }
37
38         //
39         // Returns the types we're handing metadata for
40         //
41         internal IEnumerable<Type> AttributedTypes 
42         {
43             get { return _metadata.Keys; }
44         }
45
46         //
47         // Private helper to add a portion of an existing table.
48         //
49         private static void AddAttributeMetadata(TypeMetadata newMd, TypeMetadata existingMd) 
50         {
51             if (newMd.TypeAttributes != null) 
52             {
53                 if (existingMd.TypeAttributes != null) 
54                 {
55                     existingMd.TypeAttributes.AddRange(newMd.TypeAttributes);
56                 }
57                 else 
58                 {
59                     existingMd.TypeAttributes = newMd.TypeAttributes;
60                 }
61             }
62         }
63
64         //
65         // Helper to add a enum of attributes ot an existing list
66         //
67         private static void AddAttributes(AttributeList list, IEnumerable<object> attributes) 
68         {
69             // Attributes are ordered so those at the end of the
70             // list take prececence over those at the front.
71             list.AddRange(attributes);
72         }
73
74         internal void AddCallback(Type type, AttributeCallback callback) 
75         {
76             Fx.Assert(type != null && callback != null, "type or callback parameter is null");
77             AttributeList list = GetTypeList(type);
78             list.Add(callback);
79         }
80
81         //
82         // Adds custom attrs for a type
83         //
84         internal void AddCustomAttributes(Type type, IEnumerable<object> attributes) 
85         {
86             Fx.Assert(type != null && attributes != null, "type or attributes parameter is null");
87             AddAttributes(GetTypeList(type), attributes);
88         }
89
90         //
91         // Adds custom attrs for a descriptor
92         //
93         internal void AddCustomAttributes(Type ownerType, MemberDescriptor descriptor, IEnumerable<object> attributes) 
94         {
95             Fx.Assert(ownerType != null && descriptor != null && attributes != null, "ownerType/descriptor/attributes is null");
96             AddAttributes(GetMemberList(ownerType, descriptor.Name), attributes);
97         }
98
99         //
100         // Adds custom attrs for a member
101         //
102         internal void AddCustomAttributes(Type ownerType, MemberInfo member, IEnumerable<object> attributes) 
103         {
104             Fx.Assert(ownerType != null && member != null && attributes != null, "ownertype/member/attributes parameter is null");
105             AddAttributes(GetMemberList(ownerType, member.Name), attributes);
106         }
107
108         //
109         // Adds custom attrs for a dp
110         //
111         internal void AddCustomAttributes(Type ownerType, DependencyProperty dp, IEnumerable<object> attributes) 
112         {
113             Fx.Assert(ownerType != null && dp != null && attributes != null, "ownerType/dp/attributes parameter is null");
114             AddAttributes(GetMemberList(ownerType, dp.Name), attributes);
115         }
116
117         //
118         // Adds custom attrs for a member name
119         //
120         internal void AddCustomAttributes(Type ownerType, string memberName, IEnumerable<object> attributes) 
121         {
122             Fx.Assert(ownerType != null && memberName != null && attributes != null, "ownerType/membername/attributes parameter is null");
123             AddAttributes(GetMemberList(ownerType, memberName), attributes);
124         }
125
126         //
127         // Private helper to add a portion of an existing table.
128         //
129         private static void AddMemberMetadata(TypeMetadata newMd, TypeMetadata existingMd) 
130         {
131             if (newMd.MemberAttributes != null) 
132             {
133                 if (existingMd.MemberAttributes != null) 
134                 {
135                     foreach (KeyValuePair<string, AttributeList> kv in newMd.MemberAttributes) 
136                     {
137                         AttributeList existing;
138                         if (existingMd.MemberAttributes.TryGetValue(kv.Key, out existing)) 
139                         {
140                             existing.AddRange(kv.Value);
141                         }
142                         else 
143                         {
144                             existingMd.MemberAttributes.Add(kv.Key, kv.Value);
145                         }
146                     }
147                 }
148                 else 
149                 {
150                     existingMd.MemberAttributes = newMd.MemberAttributes;
151                 }
152             }
153         }
154
155         //
156         // Adds an existing table.
157         //
158         internal void AddTable(MutableAttributeTable table) 
159         {
160             Fx.Assert(table != null, "table parameter is null");
161             foreach (KeyValuePair<Type, TypeMetadata> kv in table._metadata) 
162             {
163                 AddTypeMetadata(kv.Key, kv.Value);
164             }
165         }
166
167         //
168         // Private helper to add a portion of an existing table.
169         //
170         private void AddTypeMetadata(Type type, TypeMetadata md) 
171         {
172             TypeMetadata existing;
173             if (_metadata.TryGetValue(type, out existing)) 
174             {
175                 AddAttributeMetadata(md, existing);
176                 AddMemberMetadata(md, existing);
177             }
178             else 
179             {
180                 _metadata.Add(type, md);
181             }
182         }
183
184         //
185         // Returns true if this table contains attributes for the
186         // given type
187         //
188         internal bool ContainsAttributes(Type type) 
189         {
190             Fx.Assert(type != null, "type parameter is null");
191             return (_metadata.ContainsKey(type));
192         }
193
194         //
195         // Helper method that walks through an attribute list and expands all callbacks
196         // within it.
197         //
198         private void ExpandAttributes(Type type, AttributeList attributes) 
199         {
200             Fx.Assert(!attributes.IsExpanded, "Should not call expand attributes with an expanded list.");
201
202             // First, expand all the callbacks.  This may add more attributes
203             // into our list
204             //
205             for (int idx = 0; idx < attributes.Count; idx++) 
206             {
207                 AttributeCallback callback = attributes[idx] as AttributeCallback;
208                 while (callback != null) 
209                 {
210                     attributes.RemoveAt(idx);
211                     AttributeCallbackBuilder builder = new AttributeCallbackBuilder(this, type);
212                     callback(builder);
213
214                     if (idx < attributes.Count) 
215                     {
216                         callback = attributes[idx] as AttributeCallback;
217                     }
218                     else 
219                     {
220                         callback = null;
221                     }
222                 }
223             }
224         }
225
226         //
227         // Returns custom attributes for the type.
228         //
229         internal IEnumerable GetCustomAttributes(Type type) 
230         {
231             Fx.Assert(type != null, "type parameter is null");
232
233             AttributeList attributes = GetExpandedAttributes(type, null, delegate(Type typeToGet, object callbackParam) 
234             {
235                 TypeMetadata md;
236                 if (_metadata.TryGetValue(typeToGet, out md)) 
237                 {
238                     return md.TypeAttributes;
239                 }
240                 return null;
241             });
242
243             if (attributes != null) 
244             {
245                 return attributes.AsReadOnly();
246             }
247
248             return _empty;
249         }
250
251         //
252         // Returns custom attributes for the descriptor.
253         //
254         internal IEnumerable GetCustomAttributes(Type ownerType, MemberDescriptor descriptor) 
255         {
256             Fx.Assert(ownerType != null && descriptor != null, "ownerType or descriptor parameter is null");
257             return GetCustomAttributes(ownerType, descriptor.Name);
258         }
259
260         //
261         // Returns custom attributes for the dp.
262         //
263         internal IEnumerable GetCustomAttributes(Type ownerType, DependencyProperty dp) 
264         {
265             Fx.Assert(ownerType != null && dp != null, "ownerType or dp parameter is null");
266             return GetCustomAttributes(ownerType, dp.Name);
267         }
268
269         //
270         // Returns custom attributes for the member.
271         //
272         internal IEnumerable GetCustomAttributes(Type ownerType, MemberInfo member) 
273         {
274             Fx.Assert(ownerType != null && member != null, "ownerType or memeber parameter is null");
275             return GetCustomAttributes(ownerType, member.Name);
276         }
277
278         //
279         // Returns custom attributes for the member.
280         //
281         internal IEnumerable GetCustomAttributes(Type ownerType, string memberName) 
282         {
283             Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null");
284
285             AttributeList attributes = GetExpandedAttributes(ownerType, memberName, delegate(Type typeToGet, object callbackParam) 
286             {
287                 string name = (string)callbackParam;
288                 TypeMetadata md;
289
290                 if (_metadata.TryGetValue(typeToGet, out md)) 
291                 {
292
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.
296
297                     if (md.MemberAttributes == null && md.TypeAttributes != null && !md.TypeAttributes.IsExpanded) 
298                     {
299                         ExpandAttributes(ownerType, md.TypeAttributes);
300                     }
301
302                     if (md.MemberAttributes != null) 
303                     {
304                         AttributeList list;
305                         if (md.MemberAttributes.TryGetValue(name, out list)) 
306                         {
307                             return list;
308                         }
309                     }
310                 }
311
312                 return null;
313             });
314
315             if (attributes != null) 
316             {
317                 return attributes.AsReadOnly();
318             }
319
320             return _empty;
321         }
322
323         //
324         // Helper to demand create the attribute list ofr a dependency property.
325         //
326         private AttributeList GetMemberList(Type ownerType, string memberName) 
327         {
328             Fx.Assert(ownerType != null && memberName != null, "ownerType or memberName parameter is null");
329             TypeMetadata md = GetTypeMetadata(ownerType);
330
331             if (md.MemberAttributes == null) 
332             {
333                 md.MemberAttributes = new Dictionary<string, AttributeList>();
334             }
335
336             AttributeList list;
337             if (!md.MemberAttributes.TryGetValue(memberName, out list)) 
338             {
339                 list = new AttributeList();
340                 md.MemberAttributes.Add(memberName, list);
341             }
342
343             return list;
344         }
345
346         //
347         // Expands a type attribute table for use.
348         // Attribute tables only contain attributes for
349         // the given type, and may have callbacks embedded
350         // within them.
351         //
352         private AttributeList GetExpandedAttributes(Type type, object callbackParam, GetAttributesCallback callback) 
353         {
354
355             // Do we have attributes to expand?
356
357             AttributeList attributes = callback(type, callbackParam);
358             if (attributes != null) 
359             {
360
361                 // If these attributes haven't been expanded yet, do that
362                 // now.
363
364                 if (!attributes.IsExpanded) 
365                 {
366
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.
372
373                     lock (attributes) 
374                     {
375                         if (!attributes.IsExpanded) 
376                         {
377                             ExpandAttributes(type, attributes);
378                             attributes.IsExpanded = true;
379                         }
380                     }
381                 }
382             }
383
384             return attributes;
385         }
386
387         //
388         // Helper to demand create the attribute list for a type.
389         //
390         private AttributeList GetTypeList(Type type) 
391         {
392             Fx.Assert(type != null, "type parameter is null");
393             TypeMetadata md = GetTypeMetadata(type);
394             if (md.TypeAttributes == null) 
395             {
396                 md.TypeAttributes = new AttributeList();
397             }
398             return md.TypeAttributes;
399         }
400
401         //
402         // Helper to demand create the type metadata.
403         //
404         private TypeMetadata GetTypeMetadata(Type type) 
405         {
406             Fx.Assert(type != null, "type parameter is null");
407             TypeMetadata md;
408             if (!_metadata.TryGetValue(type, out md)) 
409             {
410                 md = new TypeMetadata();
411                 _metadata.Add(type, md);
412             }
413             return md;
414         }
415
416
417         //
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
421         // provider.
422         //
423         internal void DebugValidateProvider()
424  {
425 #if DEBUG
426             foreach (KeyValuePair<Type, TypeMetadata> kv in _metadata
427 ) {
428                 if (kv.Value.TypeAttributes != 
429 null) {
430                     AttributeCollection attrs = TypeDescriptor.GetAttributes(kv.Key);
431                     foreach (object o in kv.Value
432 .TypeAttributes) {
433                         Attribute a = o as Attribute;
434                         if (a 
435 != null) {
436                             bool found = false;
437                             foreach (Attribute a2
438  in attrs) {
439                                 if (a.TypeId.Equals
440 (a2.TypeId)) {
441                                     found = true;
442                                     break;
443                                 }
444                             }
445
446                             Fx.Assert(
447                                 found,
448                                 string.Format(CultureInfo.CurrentCulture, "Attribute {0} on type {1} is missing from provider.",
449                                 a.GetType().Name, kv.Key.Name));
450                         }
451                     }
452                 }
453
454                 if (kv.Value
455 .MemberAttributes != null) {
456                     foreach (KeyValuePair<string, AttributeList> kvDesc 
457 in kv.Value.MemberAttributes) {
458                         PropertyDescriptor p;
459                         EventDescriptor e;
460                         AttributeCollection attrs = null;
461                         string member = "unknown";
462
463                         if ((p = TypeDescriptor.GetProperties(kv.Key)[kvDesc.Key]) != null) {
464                             attrs = p.Attributes;
465                             member = p.Name;
466                         }
467                         else if ((e = TypeDescriptor.GetEvents(kv.Key)[kvDesc.Key]) != null) {
468                             attrs = e.Attributes;
469                             member = e.Name;
470                         }
471                         else if ((p = DependencyPropertyDescriptor.FromName(kvDesc.Key, kv.Key, typeof(
472 DependencyObject))) != null) {
473                             attrs = p.Attributes;
474                             member = p.Name;
475                         }
476                        
477  if (attrs != null) {
478                             foreach 
479 (object o in kvDesc.Value) {
480                                 Attribute a = o as Attribute;
481                              
482    if (a != null) {
483                                     bool found = false;
484                                   
485   foreach (Attribute a2 in attrs) {
486                                         
487 if (a.TypeId.Equals(a2.TypeId)) {
488                                             found = true;
489                                             break;
490                                         }
491                                     }
492
493                                     Fx.Assert(
494                                         found,
495                                         string.Format(CultureInfo.CurrentCulture, "Attribute {0} on member {1}.{2} is missing from provider.",
496                                         a.GetType().Name, kv.Key.Name, member));
497                                 }
498                             }
499                         }
500                     }
501                 }
502             }
503 #else
504 #endif
505         }
506
507         //
508         // Performs validation of all metadata in the table.
509         // This expands all callbacks so it can be very expensive
510         // for large tables.
511         //
512         public void ValidateTable() 
513         {
514
515             List<string> errors = null;
516
517             foreach (KeyValuePair<Type, TypeMetadata> kv in _metadata) 
518             {
519
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
523
524                 GetCustomAttributes(kv.Key);
525
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
529                 // that they exist.
530
531                 if (kv.Value.MemberAttributes != null) 
532                 {
533
534                     foreach (KeyValuePair<string, AttributeList> kvMember in kv.Value.MemberAttributes) 
535                     {
536
537                         // Validate that the attribute expansion doesn't throw
538                         GetCustomAttributes(kv.Key, kvMember.Key);
539
540                         // Validate that the member name matches a real proeprty/event and there
541                         // are no duplicates
542
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) 
546                         {
547                             p = TypeDescriptor.GetProperties(kv.Key)[kvMember.Key];
548                         }
549
550                         string errorMsg = null;
551                         if (p == null && e == null) 
552                         {
553                             errorMsg = string.Format(
554                                 CultureInfo.CurrentCulture,
555                                 Resources.Error_ValidationNoMatchingMember,
556                                 kvMember.Key, kv.Key.FullName);
557                         }
558                         else if (p != null && e != null) 
559                         {
560                             errorMsg = string.Format(
561                                 CultureInfo.CurrentCulture,
562                                 Resources.Error_ValidationAmbiguousMember,
563                                 kvMember.Key, kv.Key.FullName);
564                         }
565
566                         if (errorMsg != null) 
567                         {
568                             if (errors == null) 
569                             {
570                                 errors = new List<string>();
571                             }
572                             errors.Add(errorMsg);
573                         }
574                     }
575                 }
576             }
577
578             // Did we get any errors?
579             if (errors != null) 
580             {
581                 throw FxTrace.Exception.AsError(new AttributeTableValidationException(
582                     Resources.Error_TableValidationFailed,
583                     errors));
584             }
585         }
586
587         //
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.
592         //
593         private delegate AttributeList GetAttributesCallback(Type type, object callbackParam);
594
595         //
596         // All metadata for a type is stored here.
597         //
598         private class TypeMetadata 
599         {
600             internal AttributeList TypeAttributes;
601             internal Dictionary<string, AttributeList> MemberAttributes;
602         }
603
604         //
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.
609         //
610         private class AttributeList : List<object> 
611         {
612             private bool _isExpanded;
613
614             internal bool IsExpanded 
615             {
616                 get { return _isExpanded; }
617                 set { _isExpanded = value; }
618             }
619         }
620     }
621 }