Merge pull request #2377 from joelmartinez/docs-multiassembly-extension-fix
[mono.git] / mcs / class / referencesource / System / compmod / system / componentmodel / DebugReflectPropertyDescriptor.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DebugReflectPropertyDescriptor.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 #if DEBUG
8
9 /*
10     
11     This class exists in debug only.  It is a complete copy of the
12     V1.0 TypeDescriptor object and is used to validate that the
13     behavior of the V2.0 TypeDescriptor matches 1.0 behavior.
14     
15  */
16 namespace System.ComponentModel {
17     using System.Runtime.Serialization.Formatters;
18     using System.Runtime.InteropServices;
19     using System.Diagnostics;
20     using System;
21     using System.Collections;
22     using System.Collections.Specialized;
23     using System.Reflection;
24     using Microsoft.Win32;
25     using System.Security;
26     using System.Security.Permissions;
27     using System.ComponentModel.Design;
28     using System.ComponentModel;
29
30     /// <internalonly/>
31     /// <devdoc>
32     ///    <para>
33     ///       DebugReflectPropertyDescriptor defines a property. Properties are the main way that a user can
34     ///       set up the state of a component.
35     ///       The DebugReflectPropertyDescriptor class takes a component class that the property lives on,
36     ///       a property name, the type of the property, and various attributes for the
37     ///       property.
38     ///       For a property named XXX of type YYY, the associated component class is
39     ///       required to implement two methods of the following
40     ///       form:
41     ///    </para>
42     ///    <code>
43     /// public YYY GetXXX();
44     ///     public void SetXXX(YYY value);
45     ///    </code>
46     ///    The component class can optionally implement two additional methods of
47     ///    the following form:
48     ///    <code>
49     /// public boolean ShouldSerializeXXX();
50     ///     public void ResetXXX();
51     ///    </code>
52     ///    These methods deal with a property's default value. The ShouldSerializeXXX()
53     ///    method returns true if the current value of the XXX property is different
54     ///    than it's default value, so that it should be persisted out. The ResetXXX()
55     ///    method resets the XXX property to its default value. If the DebugReflectPropertyDescriptor
56     ///    includes the default value of the property (using the DefaultValueAttribute),
57     ///    the ShouldSerializeXXX() and ResetXXX() methods are ignored.
58     ///    If the DebugReflectPropertyDescriptor includes a reference to an editor
59     ///    then that value editor will be used to
60     ///    edit the property. Otherwise, a system-provided editor will be used.
61     ///    Various attributes can be passed to the DebugReflectPropertyDescriptor, as are described in
62     ///    Attribute.
63     ///    ReflectPropertyDescriptors can be obtained by a user programmatically through the
64     ///    ComponentManager.
65     /// </devdoc>
66     [HostProtection(SharedState = true)]
67     internal sealed class DebugReflectPropertyDescriptor : PropertyDescriptor {
68
69         private static readonly Type[] argsNone = new Type[0];
70         private static readonly object  noValue = new object();
71         
72         private static TraceSwitch PropDescCreateSwitch = new TraceSwitch("PropDescCreate", "DebugReflectPropertyDescriptor: Dump errors when creating property info");
73         private static TraceSwitch PropDescUsageSwitch  = new TraceSwitch("PropDescUsage", "DebugReflectPropertyDescriptor: Debug propertydescriptor usage");
74         private static TraceSwitch PropDescSwitch       = new TraceSwitch("PropDesc", "DebugReflectPropertyDescriptor: Debug property descriptor");
75         
76         private static readonly int BitDefaultValueQueried      = BitVector32.CreateMask();
77         private static readonly int BitGetQueried               = BitVector32.CreateMask(BitDefaultValueQueried);
78         private static readonly int BitSetQueried               = BitVector32.CreateMask(BitGetQueried);
79         private static readonly int BitShouldSerializeQueried   = BitVector32.CreateMask(BitSetQueried);
80         private static readonly int BitResetQueried             = BitVector32.CreateMask(BitShouldSerializeQueried);
81         private static readonly int BitChangedQueried           = BitVector32.CreateMask(BitResetQueried);
82         private static readonly int BitReadOnlyChecked          = BitVector32.CreateMask(BitChangedQueried);
83         private static readonly int BitAmbientValueQueried      = BitVector32.CreateMask(BitReadOnlyChecked);
84
85         internal BitVector32  state = new BitVector32();  // Contains the state bits for this proeprty descriptor.
86         Type         componentClass;             // used to determine if we should all on us or on the designer
87         Type         type;                       // the data type of the property
88         
89         internal object       defaultValue;               // the default value of the property (or noValue)
90         internal object       ambientValue;               // the ambient value of the property (or noValue)
91         
92         internal PropertyInfo propInfo;                   // the property info
93         internal MethodInfo   getMethod;                  // the property get method
94         internal MethodInfo   setMethod;                  // the property set method
95         
96         internal MethodInfo   shouldSerializeMethod;      // the should serialize method
97         internal MethodInfo   resetMethod;                // the reset property method
98         
99         EventInfo    realChangedEventInfo;       // Changed event handler on object
100         
101         Type         receiverType;               // Only set if we are an extender
102         private TypeConverter converter;
103         private object[]      editors;
104         private Type[]        editorTypes;
105         private int           editorCount;
106
107         /// <devdoc>
108         ///     The main constructor for ReflectPropertyDescriptors.
109         /// </devdoc>
110         public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type,
111                                          Attribute[] attributes)
112         : base(name, attributes) {
113         
114             Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "Creating DebugReflectPropertyDescriptor for " + componentClass.FullName + "." + name);
115             
116             try {
117                 if (type == null) {
118                     Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "type == null, name == " + name);
119                     throw new ArgumentException(SR.GetString(SR.ErrorInvalidPropertyType, name));
120                 }
121                 if (componentClass == null) {
122                     Debug.WriteLineIf(PropDescCreateSwitch.TraceVerbose, "componentClass == null, name == " + name);
123                     throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass"));
124                 }
125                 this.type = type;
126                 this.componentClass = componentClass;
127             }
128             catch (Exception t) {
129                 Debug.Fail("Property '" + name + "' on component " + componentClass.FullName + " failed to init.");
130                 Debug.Fail(t.ToString());
131                 throw t;
132             }
133         }
134         
135         /// <devdoc>
136         ///     A constructor for ReflectPropertyDescriptors that have no attributes.
137         /// </devdoc>
138         public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type, PropertyInfo propInfo, MethodInfo getMethod, MethodInfo setMethod, Attribute[] attrs) : this(componentClass, name, type, attrs) {
139             this.propInfo = propInfo;
140             this.getMethod = getMethod;
141             this.setMethod = setMethod;
142             state[BitGetQueried | BitSetQueried] = true;
143         }
144
145         /// <devdoc>
146         ///     A constructor for ReflectPropertyDescriptors that creates an extender property.
147         /// </devdoc>
148         public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type, Type receiverType, MethodInfo getMethod, MethodInfo setMethod, Attribute[] attrs) : this(componentClass, name, type, attrs) {
149             this.receiverType = receiverType;
150             this.getMethod = getMethod;
151             this.setMethod = setMethod;
152             state[BitGetQueried | BitSetQueried] = true;
153         }
154
155         /// <devdoc>
156         ///     This constructor takes an existing DebugReflectPropertyDescriptor and modifies it by merging in the
157         ///     passed-in attributes.
158         /// </devdoc>
159         public DebugReflectPropertyDescriptor(Type componentClass, PropertyDescriptor oldReflectPropertyDescriptor, Attribute[] attributes)
160         : base(oldReflectPropertyDescriptor, attributes) {
161         
162             this.componentClass = componentClass;
163             this.type = oldReflectPropertyDescriptor.PropertyType;
164
165             if (componentClass == null) {
166                 throw new ArgumentException(SR.GetString(SR.InvalidNullArgument, "componentClass"));
167             }
168
169             // If the classes are the same, we can potentially optimize the method fetch because
170             // the old property descriptor may already have it.
171             //
172             if (oldReflectPropertyDescriptor is DebugReflectPropertyDescriptor) {
173                 DebugReflectPropertyDescriptor oldProp = (DebugReflectPropertyDescriptor)oldReflectPropertyDescriptor;
174                 
175                 if (oldProp.ComponentType == componentClass) {
176                     propInfo = oldProp.propInfo;
177                     getMethod = oldProp.getMethod;
178                     setMethod = oldProp.setMethod;
179                     shouldSerializeMethod = oldProp.shouldSerializeMethod;
180                     resetMethod = oldProp.resetMethod;
181                     defaultValue = oldProp.defaultValue;
182                     ambientValue = oldProp.ambientValue;
183                     state = oldProp.state;
184                 }
185                 
186                 // Now we must figure out what to do with our default value.  First, check to see
187                 // if the caller has provided an new default value attribute.  If so, use it.  Otherwise,
188                 // just let it be and it will be picked up on demand.
189                 //
190                 if (attributes != null) {
191                     foreach(Attribute a in attributes) {
192                         if (a is DefaultValueAttribute) {
193                             defaultValue = ((DefaultValueAttribute)a).Value;
194                             state[BitDefaultValueQueried] = true;
195                         }
196                         else if (a is AmbientValueAttribute) {
197                             ambientValue = ((AmbientValueAttribute)a).Value;
198                             state[BitAmbientValueQueried] = true;
199                         }
200                     }
201                 }
202             }
203         }
204
205         /// <devdoc>
206         ///      Retrieves the ambient value for this property.
207         /// </devdoc>
208         private object AmbientValue {
209             get {
210                 if (!state[BitAmbientValueQueried]) {
211                     state[BitAmbientValueQueried] = true;
212                     Attribute a = Attributes[typeof(AmbientValueAttribute)];
213                     if (a != null) {
214                         ambientValue = ((AmbientValueAttribute)a).Value;
215                     }
216                     else {
217                         ambientValue = noValue;
218                     }
219                 }
220                 return ambientValue;
221             }
222         }
223
224         /// <devdoc>
225         ///     The EventInfo for the changed event on the component, or null if there isn't one for this property.
226         /// </devdoc>
227         private EventInfo ChangedEventValue { 
228             get {
229                 if (!state[BitChangedQueried]) {
230                     state[BitChangedQueried] = true;
231                     realChangedEventInfo = ComponentType.GetEvent(Name + "Changed", BindingFlags.Public | BindingFlags.Instance);
232                 }
233                 return realChangedEventInfo;
234             }
235
236             /* 
237             The following code has been removed to fix FXCOP violations.  The code
238             is left here incase it needs to be resurrected in the future.
239
240             set {
241                 realChangedEventInfo = value;
242                 state[BitChangedQueried] = true;
243             }
244             */
245         }
246
247         /// <devdoc>
248         ///     Retrieves the type of the component this PropertyDescriptor is bound to.
249         /// </devdoc>
250         public override Type ComponentType {
251             get {
252                 return componentClass;
253             }
254         }
255
256         /// <devdoc>
257         ///    <para>
258         ///       Gets the type converter for this property.
259         ///    </para>
260         /// </devdoc>
261         public override TypeConverter Converter {
262             get {
263                 if (converter == null) {
264                     TypeConverterAttribute attr = (TypeConverterAttribute)Attributes[typeof(TypeConverterAttribute)];
265                     if (attr.ConverterTypeName != null && attr.ConverterTypeName.Length > 0) {
266                         Type converterType = GetTypeFromName(attr.ConverterTypeName);
267                         if (converterType != null && typeof(TypeConverter).IsAssignableFrom(converterType)) {
268                             converter = (TypeConverter)CreateInstance(converterType);
269                         }
270                     }
271
272                     if (converter == null) {
273                         converter = DebugTypeDescriptor.GetConverter(PropertyType);
274                     }
275                 }
276                 return converter;
277             }
278         }
279
280         /// <devdoc>
281         ///      Retrieves the default value for this property.
282         /// </devdoc>
283         private object DefaultValue {
284             get {
285                 if (!state[BitDefaultValueQueried]) {
286                     state[BitDefaultValueQueried] = true;
287                     Attribute a = Attributes[typeof(DefaultValueAttribute)];
288                     if (a != null) {
289                         defaultValue = ((DefaultValueAttribute)a).Value;
290                     }
291                     else {
292                         defaultValue = noValue;
293                     }
294                 }
295                 return defaultValue;
296             }
297         }
298
299         /// <devdoc>
300         ///     The GetMethod for this property
301         /// </devdoc>
302         private MethodInfo GetMethodValue {
303             get {
304                 if (!state[BitGetQueried]) {
305                     state[BitGetQueried] = true;
306                     
307                     if (receiverType == null) {
308                         if (propInfo == null) {
309                             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty;
310                             propInfo = componentClass.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]);
311                         }
312                         if (propInfo != null) {
313                             getMethod = propInfo.GetGetMethod(true);
314                         }
315                         if (getMethod == null) {
316                             throw new InvalidOperationException(SR.GetString(SR.ErrorMissingPropertyAccessors, componentClass.FullName + "." + Name));
317                         }
318                     }
319                     else {
320                         getMethod = FindMethod(componentClass, "Get" + Name, new Type[] {receiverType}, type);
321                         if (getMethod == null) {
322                             throw new ArgumentException(SR.GetString(SR.ErrorMissingPropertyAccessors, Name));
323                         }
324                     }
325                 }
326                 return getMethod;
327             }
328
329             /* 
330             The following code has been removed to fix FXCOP violations.  The code
331             is left here incase it needs to be resurrected in the future.
332
333             set {
334                 state[BitGetQueried] = true;
335                 getMethod = value; 
336             } 
337             */
338         }
339         
340         /// <devdoc>
341         ///     Determines if this property is an extender property.
342         /// </devdoc>
343         private bool IsExtender {
344             get {
345                 return (receiverType != null);
346             }
347         }
348
349         /// <devdoc>
350         ///     Indicates whether this property is read only.
351         /// </devdoc>
352         public override bool IsReadOnly {
353             get {
354                 return SetMethodValue == null || ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly;
355             }
356         }
357
358         /// <devdoc>
359         ///     Retrieves the type of the property.
360         /// </devdoc>
361         public override Type PropertyType {
362             get {
363                 return type;
364             }
365         }
366
367         /// <devdoc>
368         ///     Access to the reset method, if one exists for this property.
369         /// </devdoc>
370         private MethodInfo ResetMethodValue {
371             get {
372                 if (!state[BitResetQueried]) {
373                     state[BitResetQueried] = true;
374                     
375                     Type[] args;
376                     
377                     if (receiverType == null) {
378                         args = argsNone;
379                     }
380                     else {
381                         args = new Type[] {receiverType};
382                     }
383 #if !DISABLE_CAS_USE
384                     IntSecurity.FullReflection.Assert();
385 #endif
386                     try {
387                         resetMethod = FindMethod(componentClass, "Reset" + Name, args, typeof(void), /* publicOnly= */ false);
388                     }
389                     finally {
390                         CodeAccessPermission.RevertAssert();
391                     }
392                 }
393                 return resetMethod; 
394             } 
395
396             /* 
397             The following code has been removed to fix FXCOP violations.  The code
398             is left here incase it needs to be resurrected in the future.
399
400             set {
401                 state[BitResetQueried] = true;
402                 resetMethod = value; 
403             } 
404             */
405         }
406
407         /// <devdoc>
408         ///     Accessor for the set method
409         /// </devdoc>
410         private MethodInfo SetMethodValue {
411             get {
412                 if (!state[BitSetQueried]) {
413                     state[BitSetQueried] = true;
414                     
415                     if (receiverType == null) {
416                         if (propInfo == null) {
417                             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty;
418                             propInfo = componentClass.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]);
419                         }
420                         if (propInfo != null) {
421                             setMethod = propInfo.GetSetMethod(true);
422                         }
423                     }
424                     else {
425                         setMethod = FindMethod(componentClass, "Set" + Name,
426                                                new Type[] { receiverType, type}, typeof(void));
427                     }
428                 }
429                 return setMethod; 
430             }
431
432             /* 
433             The following code has been removed to fix FXCOP violations.  The code
434             is left here incase it needs to be resurrected in the future.
435
436             set {
437                 state[BitSetQueried] = true;
438                 setMethod = value; 
439             } 
440             */
441         }
442         
443         /// <devdoc>
444         ///     Accessor for the ShouldSerialize method.
445         /// </devdoc>
446         private MethodInfo ShouldSerializeMethodValue {
447             get {
448                 if (!state[BitShouldSerializeQueried]) {
449                     state[BitShouldSerializeQueried] = true;
450                     
451                     Type[] args;
452                     
453                     if (receiverType == null) {
454                         args = argsNone;
455                     }
456                     else {
457                         args = new Type[] {receiverType};
458                     }
459 #if !DISABLE_CAS_USE
460                     IntSecurity.FullReflection.Assert();
461 #endif
462                     try {
463                         shouldSerializeMethod = FindMethod(componentClass, "ShouldSerialize" + Name,
464                                                          args, typeof(Boolean), /* publicOnly= */ false);
465                     }
466                     finally {
467                         CodeAccessPermission.RevertAssert();
468                     }
469                 }
470                 return shouldSerializeMethod;
471             }
472
473             /* 
474             The following code has been removed to fix FXCOP violations.  The code
475             is left here incase it needs to be resurrected in the future.
476
477             set {
478                 state[BitShouldSerializeQueried] = true;
479                 shouldSerializeMethod = value; 
480             }
481             */
482         }
483         
484         /// <devdoc>
485         ///     Allows interested objects to be notified when this property changes.
486         /// </devdoc>
487         public override void AddValueChanged(object component, EventHandler handler) {
488             if (component == null) throw new ArgumentNullException("component");
489             if (handler == null) throw new ArgumentNullException("handler");
490             
491             EventInfo changedEvent = ChangedEventValue;
492             if (changedEvent != null) {
493                 changedEvent.AddEventHandler(component, handler);
494             }
495             else {
496                 base.AddValueChanged(component, handler);
497             }
498         }
499
500         internal bool ExtenderCanResetValue(IExtenderProvider provider, object component) {
501             if (DefaultValue != noValue) {
502                 return !object.Equals(ExtenderGetValue(provider, component),defaultValue);
503             }
504             
505             MethodInfo reset = ResetMethodValue;
506             if (reset != null) {
507                 MethodInfo shouldSerialize = ShouldSerializeMethodValue;
508                 if (shouldSerialize != null) {
509                     try {
510                         provider = (IExtenderProvider)GetDebugInvokee(componentClass, provider);
511                         return (bool)shouldSerialize.Invoke(provider, new object[] { component});
512                     }
513                     catch {}
514                 }
515             }
516             else {
517                 return true;
518             }
519             return false;
520         }
521
522         internal Type ExtenderGetReceiverType() {
523             return receiverType;
524         }
525
526         internal Type ExtenderGetType(IExtenderProvider provider) {
527             return PropertyType;
528         }
529
530         internal object ExtenderGetValue(IExtenderProvider provider, object component) {
531             if (provider != null) {
532                 provider = (IExtenderProvider)GetDebugInvokee(componentClass, provider);
533                 return GetMethodValue.Invoke(provider, new object[] { component});
534             }
535             return null;
536         }
537
538         internal void ExtenderResetValue(IExtenderProvider provider, object component, PropertyDescriptor notifyDesc) {
539             if (DefaultValue != noValue) {
540                 ExtenderSetValue(provider, component, DefaultValue, notifyDesc);
541             }
542             else if (AmbientValue != noValue) {
543                 ExtenderSetValue(provider, component, AmbientValue, notifyDesc);
544             }
545             else if (ResetMethodValue != null) {
546                 ISite site = GetSite(component);
547                 IComponentChangeService changeService = null;
548                 object oldValue = null;
549                 object newValue;
550
551                 // Announce that we are about to change this component
552                 //
553                 if (site != null) {
554                     changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
555                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
556                 }
557
558                 // Make sure that it is ok to send the onchange events
559                 //
560                 if (changeService != null) {
561                     oldValue = ExtenderGetValue(provider, component);
562                     try {
563                         changeService.OnComponentChanging(component, notifyDesc);
564                     }
565                     catch (CheckoutException coEx) {
566                         if (coEx == CheckoutException.Canceled) {
567                             return;
568                         }
569                         throw coEx;
570                     }
571                 }
572
573                 provider = (IExtenderProvider)GetDebugInvokee(componentClass, provider);
574                 if (ResetMethodValue != null) {
575                     ResetMethodValue.Invoke(provider, new object[] { component});
576
577                     // Now notify the change service that the change was successful.
578                     //
579                     if (changeService != null) {
580                         newValue = ExtenderGetValue(provider, component);
581                         changeService.OnComponentChanged(component, notifyDesc, oldValue, newValue);
582                     }
583                 }
584             }
585         }
586
587         internal void ExtenderSetValue(IExtenderProvider provider, object component, object value, PropertyDescriptor notifyDesc) {
588             if (provider != null) {
589
590                 ISite site = GetSite(component);
591                 IComponentChangeService changeService = null;
592                 object oldValue = null;
593
594                 // Announce that we are about to change this component
595                 //
596                 if (site != null) {
597                     changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
598                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
599                 }
600
601                 // Make sure that it is ok to send the onchange events
602                 //
603                 if (changeService != null) {
604                     oldValue = ExtenderGetValue(provider, component);
605                     try {
606                         changeService.OnComponentChanging(component, notifyDesc);
607                     }
608                     catch (CheckoutException coEx) {
609                         if (coEx == CheckoutException.Canceled) {
610                             return;
611                         }
612                         throw coEx;
613                     }
614                 }
615
616                 provider = (IExtenderProvider)GetDebugInvokee(componentClass, provider);
617
618                 if (SetMethodValue != null) {
619                     SetMethodValue.Invoke(provider, new object[] { component, value});
620
621                     // Now notify the change service that the change was successful.
622                     //
623                     if (changeService != null) {
624                         changeService.OnComponentChanged(component, notifyDesc, oldValue, value);
625                     }
626                 }
627             }
628         }
629
630         internal bool ExtenderShouldSerializeValue(IExtenderProvider provider, object component) {
631         
632
633             provider = (IExtenderProvider)GetDebugInvokee(componentClass, provider);
634
635             if (IsReadOnly) {
636                 if (ShouldSerializeMethodValue != null) {
637                     try {
638                         return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] {component});
639                     }
640                     catch {}
641                 }
642                 return Attributes.Contains(DesignerSerializationVisibilityAttribute.Content);
643             }
644             else if (DefaultValue == noValue) {
645                 if (ShouldSerializeMethodValue != null) {
646                     try {
647                         return (bool)ShouldSerializeMethodValue.Invoke(provider, new object[] {component});
648                     }
649                     catch {}
650                 }
651                 return true;
652             }
653             return !object.Equals(DefaultValue, ExtenderGetValue(provider, component));
654         }
655
656         /// <devdoc>
657         ///     Indicates whether reset will change the value of the component.  If there
658         ///     is a DefaultValueAttribute, then this will return true if getValue returns
659         ///     something different than the default value.  If there is a reset method and
660         ///     a ShouldSerialize method, this will return what ShouldSerialize returns.
661         ///     If there is just a reset method, this always returns true.  If none of these
662         ///     cases apply, this returns false.
663         /// </devdoc>
664         public override bool CanResetValue(object component) {
665             if (IsExtender) {
666                 return false;
667             }
668
669             if (DefaultValue != noValue) {
670                 return !object.Equals(GetValue(component),DefaultValue);
671             }
672             
673             if (ResetMethodValue != null) {
674                 if (ShouldSerializeMethodValue != null) {
675                     component = GetDebugInvokee(componentClass, component);
676                     try {
677                         return (bool)ShouldSerializeMethodValue.Invoke(component, null);
678                     }
679                     catch {}
680                 }
681                 return true;
682             }
683             
684             if (AmbientValue != noValue) {
685                 return ShouldSerializeValue(component);
686             }
687             
688             return false;
689         }
690
691         protected override void FillAttributes(IList attributes) {
692             Debug.Assert(componentClass != null, "Must have a component class for FillAttributes");
693
694             //
695             // The order that we fill in attributes is critical.  The list of attributes will be
696             // filtered so that matching attributes at the end of the list replace earlier matches
697             // (last one in wins).  Therefore, the three categories of attributes we add must be
698             // added as follows:
699             //
700             // 1.  Attributes of the property type.  These are the lowest level and should be
701             //     overwritten by any newer attributes.
702             //
703             // 2.  Attributes of the property itself, from base class to most derived.  This way
704             //     derived class attributes replace base class attributes.
705             //
706             // 3.  Attributes from our base MemberDescriptor.  While this seems opposite of what
707             //     we want, MemberDescriptor only has attributes if someone passed in a new
708             //     set in the constructor.  Therefore, these attributes always
709             //     supercede existing values.
710             //
711             
712             
713             // We need to include attributes from the type of the property.
714             //
715             foreach (Attribute typeAttr in DebugTypeDescriptor.GetAttributes(PropertyType)) {
716                 attributes.Add(typeAttr);
717             }
718         
719             // NOTE : Must look at method OR property, to handle the case of Extender properties...
720             //
721             // Note : Because we are using BindingFlags.DeclaredOnly it is more effcient to re-aquire
722             //      : the property info, rather than use the one we have cached.  The one we have cached
723             //      : may ave come from a base class, meaning we will request custom metadata for this
724             //      : class twice.
725
726             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly;
727             Type currentReflectType = componentClass;
728             int depth = 0;
729             
730             // First, calculate the depth of the object hierarchy.  We do this so we can do a single
731             // object create for an array of attributes.
732             //
733             while(currentReflectType != null && currentReflectType != typeof(object)) {
734                 depth++;
735                 currentReflectType = currentReflectType.BaseType;
736             }
737             
738             // Now build up an array in reverse order
739             //
740             if (depth > 0) {
741                 currentReflectType = componentClass;
742                 object[][] attributeStack = new object[depth][];
743                 
744                 while(currentReflectType != null && currentReflectType != typeof(object)) {
745                 
746                     MemberInfo memberInfo = null;
747                     
748                     // Fill in our member info so we can get at the custom attributes.
749                     //
750                     if (IsExtender) {
751                         memberInfo = currentReflectType.GetMethod("Get" + Name, bindingFlags);
752                     }
753                     else {
754                         memberInfo = currentReflectType.GetProperty(Name, bindingFlags, null, PropertyType, new Type[0], new ParameterModifier[0]);
755                     }
756                     
757                     // Get custom attributes for the member info.
758                     //
759                     if (memberInfo != null) {
760                         attributeStack[--depth] = DebugTypeDescriptor.GetCustomAttributes(memberInfo);
761                     }
762                     
763                     // Ready for the next loop iteration.
764                     //
765                     currentReflectType = currentReflectType.BaseType;
766                 }
767                    
768                 // Now trawl the attribute stack so that we add attributes
769                 // from base class to most derived.
770                 //
771                 foreach(object[] attributeArray in attributeStack) {
772                     if (attributeArray != null) {
773                         foreach(object attr in attributeArray) {
774                             if (attr is Attribute) {
775                                 attributes.Add(attr);
776                             }
777                         }
778                     }
779                 }
780             }
781             
782             // Include the base attributes.  These override all attributes on the actual
783             // property, so we want to add them last.
784             //
785             base.FillAttributes(attributes);
786             
787             // Finally, override any form of ReadOnlyAttribute.  
788             //
789             if (!state[BitReadOnlyChecked]) {
790                 state[BitReadOnlyChecked] = true;
791                 if (SetMethodValue == null) {
792                     attributes.Add(ReadOnlyAttribute.Yes);
793                 }
794             }
795         }
796         
797         /// <devdoc>
798         ///    Retrieves the properties 
799         /// </devdoc>
800         public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) {
801             if (instance == null) {
802                 return DebugTypeDescriptor.GetProperties(PropertyType, filter);
803             }
804             else {
805                 return DebugTypeDescriptor.GetProperties(instance, filter);
806             }
807         }
808
809         /// <devdoc>
810         ///    <para>
811         ///       Gets
812         ///       the component
813         ///       that a method should be invoked on.
814         ///    </para>
815         /// </devdoc>
816         private static object GetDebugInvokee(Type componentClass, object component) {
817
818             // We delve into the component's designer only if it is a component and if
819             // the component we've been handed is not an instance of this property type.
820             //
821             if (!componentClass.IsInstanceOfType(component) && component is IComponent) {
822                 ISite site = ((IComponent)component).Site;
823                 if (site != null && site.DesignMode) {
824                     IDesignerHost host = (IDesignerHost)site.GetService(typeof(IDesignerHost));
825                     if (host != null) {
826                         object designer = host.GetDesigner((IComponent)component);
827
828                         // We only use the designer if it has a compatible class.  If we
829                         // got here, we're probably hosed because the user just passed in
830                         // an object that this PropertyDescriptor can't munch on, but it's
831                         // clearer to use that object instance instead of it's designer.
832                         //
833                         if (designer != null && componentClass.IsInstanceOfType(designer)) {
834                             component = designer;
835                         }
836                     }
837                 }
838             }
839
840             Debug.Assert(component != null, "Attempt to invoke on null component");
841             return component;
842         }
843
844         /// <devdoc>
845         ///    <para>
846         ///       Gets an editor of the specified type.
847         ///    </para>
848         /// </devdoc>
849         public override object GetEditor(Type editorBaseType) {
850             object editor = null;
851
852             // Check the editors we've already created for this type.
853             //
854             if (editorTypes != null) {
855                 for (int i = 0; i < editorCount; i++) {
856                     if (editorTypes[i] == editorBaseType) {
857                         return editors[i];
858                     }
859                 }
860             }
861
862             // If one wasn't found, then we must go through the attributes.
863             //
864             if (editor == null) {
865                 for (int i = 0; i < Attributes.Count; i++) {
866
867                     if (!(Attributes[i] is EditorAttribute)) {
868                         continue;
869                     }
870
871                     EditorAttribute attr = (EditorAttribute)Attributes[i];
872                     Type editorType = GetTypeFromName(attr.EditorBaseTypeName);
873
874                     if (editorBaseType == editorType) {
875                         Type type = GetTypeFromName(attr.EditorTypeName);
876                         if (type != null) {
877                             editor = CreateInstance(type);
878                             break;
879                         }
880                     }
881                 }
882                 
883                 // Now, if we failed to find it in our own attributes, go to the
884                 // component descriptor.
885                 //
886                 if (editor == null) {
887                     editor = DebugTypeDescriptor.GetEditor(PropertyType, editorBaseType);
888                 }
889                 
890                 // Now, another slot in our editor cache for next time
891                 //
892                 if (editorTypes == null) {
893                     editorTypes = new Type[5];
894                     editors = new object[5];
895                 }
896
897                 if (editorCount >= editorTypes.Length) {
898                     Type[] newTypes = new Type[editorTypes.Length * 2];
899                     object[] newEditors = new object[editors.Length * 2];
900                     Array.Copy(editorTypes, newTypes, editorTypes.Length);
901                     Array.Copy(editors, newEditors, editors.Length);
902                     editorTypes = newTypes;
903                     editors = newEditors;
904                 }
905
906                 editorTypes[editorCount] = editorBaseType;
907                 editors[editorCount++] = editor;
908             }
909
910             return editor;
911         }
912
913         /// <devdoc>
914         ///     Retrieves the current value of the property on component,
915         ///     invoking the getXXX method.  An exception in the getXXX
916         ///     method will pass through.
917         /// </devdoc>
918         public override object GetValue(object component) {
919 #if DEBUG
920             if (PropDescUsageSwitch.TraceVerbose) {
921                 string compName = "(null)";
922                 if (component != null)
923                     compName = component.ToString();
924
925                 Debug.WriteLine("[" + Name + "]: GetValue(" + compName + ")");
926             }
927 #endif
928
929             if (IsExtender) {
930                 Debug.WriteLineIf(PropDescUsageSwitch.TraceVerbose, "[" + Name + "]:   ---> returning: null");
931                 return null;
932             }
933
934             Debug.Assert(component != null, "GetValue must be given a component");
935
936             if (component != null) {
937                 component = GetDebugInvokee(componentClass, component);
938                 
939
940                 try {
941                     return GetMethodValue.Invoke(component, null);
942                 }
943                 catch (Exception t) {
944                     
945                     string name = null;
946                     if (component is IComponent) {
947
948                         ISite site = ((IComponent)component).Site;
949                         if (site != null && site.Name != null) {
950                             name = site.Name;
951                         }
952                     }
953                     
954                     if (name == null) {
955                         name = component.GetType().FullName;
956                     }
957                     
958                     if (t is TargetInvocationException) {
959                         t = t.InnerException;
960                     }
961                     
962                     string message = t.Message;
963                     if (message == null) {
964                         message = t.GetType().Name;
965                     }
966                     
967                     throw new TargetInvocationException(SR.GetString(SR.ErrorPropertyAccessorException, Name, name, message), t);
968                 }
969             }
970             Debug.WriteLineIf(PropDescUsageSwitch.TraceVerbose, "[" + Name + "]:   ---> returning: null");
971             return null;
972         }
973
974         /// <devdoc>
975         ///     This should be called by your property descriptor implementation
976         ///     when the property value has changed.
977         /// </devdoc>
978         protected override void OnValueChanged(object component, EventArgs e) {
979             if (state[BitChangedQueried] && realChangedEventInfo == null) {
980                 base.OnValueChanged(component, e);
981             }
982         }
983
984         /// <devdoc>
985         ///     Allows interested objects to be notified when this property changes.
986         /// </devdoc>
987         public override void RemoveValueChanged(object component, EventHandler handler) {
988             if (component == null) throw new ArgumentNullException("component");
989             if (handler == null) throw new ArgumentNullException("handler");
990             
991             EventInfo changedEvent = ChangedEventValue;
992             if (changedEvent != null) {
993                 changedEvent.RemoveEventHandler(component, handler);
994             }
995             else {
996                 base.RemoveValueChanged(component, handler);
997             }
998         }
999
1000         /// <devdoc>
1001         ///     Will reset the default value for this property on the component.  If
1002         ///     there was a default value passed in as a DefaultValueAttribute, that
1003         ///     value will be set as the value of the property on the component.  If
1004         ///     there was no default value passed in, a ResetXXX method will be looked
1005         ///     for.  If one is found, it will be invoked.  If one is not found, this
1006         ///     is a nop.
1007         /// </devdoc>
1008         public override void ResetValue(object component) {
1009             object invokee = GetDebugInvokee(componentClass, component);
1010
1011             if (DefaultValue != noValue) {
1012                 SetValue(component, DefaultValue);
1013             }
1014             else if (AmbientValue != noValue) {
1015                 SetValue(component, AmbientValue);
1016             }
1017             else if (ResetMethodValue != null) {
1018                 ISite site = GetSite(component);
1019                 IComponentChangeService changeService = null;
1020                 object oldValue = null;
1021                 object newValue;
1022
1023                 // Announce that we are about to change this component
1024                 //
1025                 if (site != null) {
1026                     changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
1027                     Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
1028                 }
1029
1030                 // Make sure that it is ok to send the onchange events
1031                 //
1032                 if (changeService != null) {
1033                     oldValue = GetMethodValue.Invoke(invokee, (object[])null);
1034                     try {
1035                         changeService.OnComponentChanging(component, this);
1036                     }
1037                     catch (CheckoutException coEx) {
1038                         if (coEx == CheckoutException.Canceled) {
1039                             return;
1040                         }
1041                         throw coEx;
1042                     }
1043                     catch {
1044                         throw;
1045                     }
1046
1047                 }
1048
1049                 if (ResetMethodValue != null) {
1050                     ResetMethodValue.Invoke(invokee, (object[])null);
1051
1052                     // Now notify the change service that the change was successful.
1053                     //
1054                     if (changeService != null) {
1055                         newValue = GetMethodValue.Invoke(invokee, (object[])null);
1056                         changeService.OnComponentChanged(component, this, oldValue, newValue);
1057                     }
1058                 }
1059             }
1060         }
1061
1062         /// <devdoc>
1063         ///     This will set value to be the new value of this property on the
1064         ///     component by invoking the setXXX method on the component.  If the
1065         ///     value specified is invalid, the component should throw an exception
1066         ///     which will be passed up.  The component designer should design the
1067         ///     property so that getXXX following a setXXX should return the value
1068         ///     passed in if no exception was thrown in the setXXX call.
1069         /// </devdoc>
1070         public override void SetValue(object component, object value) {
1071 #if DEBUG
1072             if (PropDescUsageSwitch.TraceVerbose) {
1073                 string compName = "(null)";
1074                 string valName  = "(null)";
1075
1076                 if (component != null)
1077                     compName = component.ToString();
1078                 if (value != null)
1079                     valName = value.ToString();
1080
1081                 Debug.WriteLine("[" + Name + "]: SetValue(" + compName + ", " + valName + ")");
1082             }
1083 #endif
1084             if (component != null) {
1085                 ISite site = GetSite(component);
1086                 IComponentChangeService changeService = null;
1087                 object oldValue = null;
1088
1089                 object invokee = GetDebugInvokee(componentClass, component);
1090
1091                 Debug.Assert(!IsReadOnly, "SetValue attempted on read-only property [" + Name + "]");
1092                 if (!IsReadOnly) {
1093
1094                     // Announce that we are about to change this component
1095                     //
1096                     if (site != null) {
1097                         changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
1098                         Debug.Assert(!CompModSwitches.CommonDesignerServices.Enabled || changeService != null, "IComponentChangeService not found");
1099                     }
1100
1101
1102                     // Make sure that it is ok to send the onchange events
1103                     //
1104                     if (changeService != null) {
1105                         oldValue = GetMethodValue.Invoke(invokee, null);
1106                         try {
1107                             changeService.OnComponentChanging(component, this);
1108                         }
1109                         catch (CheckoutException coEx) {
1110                             if (coEx == CheckoutException.Canceled) {
1111                                 return;
1112                             }
1113                             throw coEx;
1114                         }
1115                     }
1116
1117                     try {
1118                         try {
1119                             SetMethodValue.Invoke(invokee, new object[]{value});
1120                             OnValueChanged(invokee, EventArgs.Empty);
1121                         }
1122                         catch (Exception t) {
1123                             // Give ourselves a chance to unwind properly before rethrowing the exception (bug# 20221).
1124                             //
1125                             value = oldValue;
1126                             
1127                             // If there was a problem setting the controls property then we get:
1128                             // ArgumentException (from properties set method)
1129                             // ==> Becomes inner exception of TargetInvocationException
1130                             // ==> caught here
1131
1132                             if (t is TargetInvocationException && t.InnerException != null) {
1133                                 // Propagate the original exception up
1134                                 throw t.InnerException;
1135                             }
1136                             else {
1137                                 throw t;
1138                             }
1139                         }
1140                     }
1141                     finally {
1142                         // Now notify the change service that the change was successful.
1143                         //
1144                         if (changeService != null) {
1145                             changeService.OnComponentChanged(component, this, oldValue, value);
1146                         }
1147                     }
1148                 }
1149             }
1150         }
1151
1152         /// <devdoc>
1153         ///     Indicates whether the value of this property needs to be persisted. In
1154         ///     other words, it indicates whether the state of the property is distinct
1155         ///     from when the component is first instantiated. If there is a default
1156         ///     value specified in this DebugReflectPropertyDescriptor, it will be compared against the
1157         ///     property's current value to determine this.  If there is't, the
1158         ///     ShouldSerializeXXX method is looked for and invoked if found.  If both
1159         ///     these routes fail, true will be returned.
1160         ///
1161         ///     If this returns false, a tool should not persist this property's value.
1162         /// </devdoc>
1163         public override bool ShouldSerializeValue(object component) {
1164
1165             component = GetDebugInvokee(componentClass, component);
1166
1167             if (IsReadOnly) {
1168                 if (ShouldSerializeMethodValue != null) {
1169                     try {
1170                         return (bool)ShouldSerializeMethodValue.Invoke(component, null);
1171                     }
1172                     catch {}
1173                 }
1174                 return Attributes.Contains(DesignerSerializationVisibilityAttribute.Content);
1175             }
1176             else if (DefaultValue == noValue) {
1177                 if (ShouldSerializeMethodValue != null) {
1178                     try {
1179                         return (bool)ShouldSerializeMethodValue.Invoke(component, null);
1180                     }
1181                     catch {}
1182                 }
1183                 return true;
1184             }
1185             return !object.Equals(DefaultValue, GetValue(component));
1186         }
1187
1188
1189         /* 
1190             The following code has been removed to fix FXCOP violations.  The code
1191             is left here incase it needs to be resurrected in the future.
1192
1193         /// <devdoc>
1194         ///     A constructor for ReflectPropertyDescriptors that have no attributes.
1195         /// </devdoc>
1196         public DebugReflectPropertyDescriptor(Type componentClass, string name, Type type) : this(componentClass, name, type, (Attribute[])null) {
1197         }
1198         */
1199     }
1200 }
1201
1202 #endif