Merge pull request #3040 from xmcclure/debugger-step-recursive
[mono.git] / mcs / class / referencesource / System / compmod / system / componentmodel / TypeConverter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="TypeConverter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 /*
8  */
9 namespace System.ComponentModel {
10     using Microsoft.Win32;
11     using System.Collections;
12     using System.Configuration;
13     using System.ComponentModel.Design.Serialization;
14     using System.Diagnostics;
15     using System.Diagnostics.CodeAnalysis;
16     using System.Globalization;
17     using System.Runtime.InteropServices;
18     using System.Runtime.Remoting;
19     using System.Runtime.Serialization.Formatters;
20     using System.Security.Permissions;
21
22     /// <devdoc>
23     ///    <para>Converts the value of an object into a different data type.</para>
24     /// </devdoc>
25     [HostProtection(SharedState = true)]
26     [System.Runtime.InteropServices.ComVisible(true)]
27     public class TypeConverter {
28
29         private const string s_UseCompatibleTypeConverterBehavior = "UseCompatibleTypeConverterBehavior";
30         private static volatile bool useCompatibleTypeConversion = false;
31         private static volatile bool firstLoadAppSetting = true;
32         private static object loadAppSettingLock = new Object();
33
34         private static bool UseCompatibleTypeConversion {
35             get {
36 #if !MONO
37                 if (firstLoadAppSetting) {
38                     lock (loadAppSettingLock) {
39                         if (firstLoadAppSetting) {
40                             string useCompatibleConfig = ConfigurationManager.AppSettings[s_UseCompatibleTypeConverterBehavior];
41
42                             try {
43                                 if (!String.IsNullOrEmpty(useCompatibleConfig)) {
44                                     useCompatibleTypeConversion = bool.Parse(useCompatibleConfig.Trim());
45                                 }
46                             }
47                             catch {
48                                 // we get any exception, then eat out the exception, and use the new TypeConverter.
49                                 useCompatibleTypeConversion = false;
50                             }
51
52                             firstLoadAppSetting = false;
53                         }
54                     }
55                 }
56 #endif
57                 return useCompatibleTypeConversion;
58             }
59         }
60
61         /// <devdoc>
62         ///    <para>Gets a value indicating whether this converter can convert an object in the
63         ///       given source type to the native type of the converter.</para>
64         /// </devdoc>
65         public bool CanConvertFrom(Type sourceType) {
66             return CanConvertFrom(null, sourceType);
67         }
68
69         /// <devdoc>
70         ///    <para>Gets a value indicating whether this converter can
71         ///       convert an object in the given source type to the native type of the converter
72         ///       using the context.</para>
73         /// </devdoc>
74         public virtual bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
75             if (sourceType == typeof(InstanceDescriptor)) {
76                 return true;
77             }
78             return false;
79         }
80         
81         /// <devdoc>
82         ///    <para>Gets a value indicating whether this converter can
83         ///       convert an object to the given destination type using the context.</para>
84         /// </devdoc>
85         public bool CanConvertTo(Type destinationType) {
86             return CanConvertTo(null, destinationType);
87         }
88
89         /// <devdoc>
90         ///    <para>Gets a value indicating whether this converter can
91         ///       convert an object to the given destination type using the context.</para>
92         /// </devdoc>
93         public virtual bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
94             return (destinationType == typeof(string));
95         }
96
97         /// <devdoc>
98         ///    <para>Converts the given value
99         ///       to the converter's native type.</para>
100         /// </devdoc>
101         public object ConvertFrom(object value) {
102             return ConvertFrom(null, CultureInfo.CurrentCulture, value);
103         }
104
105         /// <devdoc>
106         ///    <para>Converts the given object to the converter's native type.</para>
107         /// </devdoc>
108         public virtual object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
109             InstanceDescriptor id = value as InstanceDescriptor;
110             if (id != null) {
111                 return id.Invoke();
112             }
113             throw GetConvertFromException(value);
114         }
115
116         /// <devdoc>
117         ///    Converts the given string to the converter's native type using the invariant culture.
118         /// </devdoc>
119         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
120         public object ConvertFromInvariantString(string text) {
121             return ConvertFromString(null, CultureInfo.InvariantCulture, text);
122         }
123
124         /// <devdoc>
125         ///    Converts the given string to the converter's native type using the invariant culture.
126         /// </devdoc>
127         public object ConvertFromInvariantString(ITypeDescriptorContext context, string text) {
128             return ConvertFromString(context, CultureInfo.InvariantCulture, text);
129         }
130
131         /// <devdoc>
132         ///    <para>Converts the specified text into an object.</para>
133         /// </devdoc>
134         public object ConvertFromString(string text) {
135             return ConvertFrom(null, null, text);
136         }
137
138         /// <devdoc>
139         ///    <para>Converts the specified text into an object.</para>
140         /// </devdoc>
141         public object ConvertFromString(ITypeDescriptorContext context, string text) {
142             return ConvertFrom(context, CultureInfo.CurrentCulture, text);
143         }
144
145         /// <devdoc>
146         ///    <para>Converts the specified text into an object.</para>
147         /// </devdoc>
148         public object ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string text) {
149             return ConvertFrom(context, culture, text);
150         }
151
152         /// <devdoc>
153         ///    <para>Converts the given
154         ///       value object to the specified destination type using the arguments.</para>
155         /// </devdoc>
156         public object ConvertTo(object value, Type destinationType) {
157             return ConvertTo(null, null, value, destinationType);
158         }
159
160         /// <devdoc>
161         ///    <para>Converts the given value object to
162         ///       the specified destination type using the specified context and arguments.</para>
163         /// </devdoc>
164         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // keep CultureInfo
165         public virtual object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
166             if (destinationType == null) {
167                 throw new ArgumentNullException("destinationType");
168             }
169
170             if (destinationType == typeof(string)) {
171                 if (value == null) {
172                     return String.Empty;
173                 }
174
175                 // Pre-whidbey we just did a ToString() here.  To minimize the chance of a breaking change we
176                 // still send requests for the CurrentCulture to ToString() (which should return the same).
177                 if(culture != null && culture != CultureInfo.CurrentCulture) {
178                     // VSWhidbey 75433 - If the object is IFormattable, use this interface to convert to string
179                     // so we use the specified culture rather than the CurrentCulture like object.ToString() does.
180                     IFormattable formattable = value as IFormattable;
181                     if(formattable != null) {
182                         return formattable.ToString(/* format = */ null, /* formatProvider = */ culture);
183                     }
184                 }
185                 return value.ToString();
186             }
187             throw GetConvertToException(value, destinationType);
188         }
189
190         /// <devdoc>
191         ///    <para>Converts the specified value to a culture-invariant string representation.</para>
192         /// </devdoc>
193         public string ConvertToInvariantString(object value) {
194             return ConvertToString(null, CultureInfo.InvariantCulture, value);
195         }
196
197         /// <devdoc>
198         ///    <para>Converts the specified value to a culture-invariant string representation.</para>
199         /// </devdoc>
200         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
201         public string ConvertToInvariantString(ITypeDescriptorContext context, object value) {
202             return ConvertToString(context, CultureInfo.InvariantCulture, value);
203         }
204
205         /// <devdoc>
206         ///    <para>Converts the specified value to a string representation.</para>
207         /// </devdoc>
208         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
209         public string ConvertToString(object value) {
210             return (string)ConvertTo(null, CultureInfo.CurrentCulture, value, typeof(string));
211         }
212
213         /// <devdoc>
214         ///    <para>Converts the specified value to a string representation.</para>
215         /// </devdoc>
216         public string ConvertToString(ITypeDescriptorContext context, object value) {
217             return (string)ConvertTo(context, CultureInfo.CurrentCulture, value, typeof(string));
218         }
219
220         /// <devdoc>
221         ///    <para>Converts the specified value to a string representation.</para>
222         /// </devdoc>
223         public string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, object value) {
224             return (string)ConvertTo(context, culture, value, typeof(string));
225         }
226
227         /// <devdoc>
228         /// <para>Re-creates an <see cref='System.Object'/> given a set of property values for the object.</para>
229         /// </devdoc>
230         public object CreateInstance(IDictionary propertyValues) {
231             return CreateInstance(null, propertyValues);
232         }
233
234         /// <devdoc>
235         /// <para>Re-creates an <see cref='System.Object'/> given a set of property values for the
236         ///    object.</para>
237         /// </devdoc>
238         public virtual object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) {
239             return null;
240         }
241
242         /// <devdoc>
243         ///    <para>
244         ///       Gets a suitable exception to throw when a conversion cannot
245         ///       be performed.
246         ///    </para>
247         /// </devdoc>
248         protected Exception GetConvertFromException(object value) {
249             string valueTypeName;
250
251             if (value == null) {
252                 valueTypeName = SR.GetString(SR.ToStringNull);
253             }
254             else {
255                 valueTypeName = value.GetType().FullName;
256             }
257
258             throw new NotSupportedException(SR.GetString(SR.ConvertFromException, GetType().Name, valueTypeName));
259         }
260
261         /// <devdoc>
262         ///    <para>Retrieves a suitable exception to throw when a conversion cannot
263         ///       be performed.</para>
264         /// </devdoc>
265         protected Exception GetConvertToException(object value, Type destinationType) {
266             string valueTypeName;
267
268             if (value == null) {
269                 valueTypeName = SR.GetString(SR.ToStringNull);
270             }
271             else {
272                 valueTypeName = value.GetType().FullName;
273             }
274
275             throw new NotSupportedException(SR.GetString(SR.ConvertToException, GetType().Name, valueTypeName, destinationType.FullName));
276         }
277         
278         /// <devdoc>
279         ///    <para>Gets a value indicating whether changing a value on this 
280         ///       object requires a call to <see cref='System.ComponentModel.TypeConverter.CreateInstance'/>
281         ///       to create a new value.</para>
282         /// </devdoc>
283         public bool GetCreateInstanceSupported() {
284             return GetCreateInstanceSupported(null);
285         }
286
287         /// <devdoc>
288         ///    <para>Gets a value indicating whether changing a value on this object requires a 
289         ///       call to <see cref='System.ComponentModel.TypeConverter.CreateInstance'/> to create a new value,
290         ///       using the specified context.</para>
291         /// </devdoc>
292         public virtual bool GetCreateInstanceSupported(ITypeDescriptorContext context) {
293             return false;
294         }
295
296         /// <devdoc>
297         ///    <para>Gets a collection of properties for the type of array specified by the value
298         ///       parameter.</para>
299         /// </devdoc>
300         public PropertyDescriptorCollection GetProperties(object value) {
301             return GetProperties(null, value);
302         }
303
304         /// <devdoc>
305         ///    <para>Gets a collection of
306         ///       properties for the type of array specified by the value parameter using the specified
307         ///       context.</para>
308         /// </devdoc>
309         public PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value) {
310             return GetProperties(context, value, new Attribute[] {BrowsableAttribute.Yes});
311         }  
312         
313         /// <devdoc>
314         ///    <para>Gets a collection of properties for
315         ///       the type of array specified by the value parameter using the specified context and
316         ///       attributes.</para>
317         /// </devdoc>
318         public virtual PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) {
319             return null;
320         }
321        
322         /// <devdoc>
323         ///    <para>
324         ///       Gets a value indicating whether this object supports properties.
325         ///    </para>
326         /// </devdoc>
327         public bool GetPropertiesSupported() {
328             return GetPropertiesSupported(null);
329         }
330
331         /// <devdoc>
332         ///    <para>Gets a value indicating
333         ///       whether this object supports properties using the
334         ///       specified context.</para>
335         /// </devdoc>
336         public virtual bool GetPropertiesSupported(ITypeDescriptorContext context) {
337             return false;
338         }
339         
340         /// <devdoc>
341         ///    <para> Gets a collection of standard values for the data type this type
342         ///       converter is designed for.</para>
343         /// </devdoc>
344         public ICollection GetStandardValues() {
345             return GetStandardValues(null);
346         }
347
348         /// <devdoc>
349         ///    <para>Gets a collection of standard values for the data type this type converter is
350         ///       designed for.</para>
351         /// </devdoc>
352         public virtual StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
353             return null;
354         }
355
356         /// <devdoc>
357         ///    <para>Gets a value indicating whether the collection of standard values returned from
358         ///    <see cref='System.ComponentModel.TypeConverter.GetStandardValues'/> is an exclusive list. </para>
359         /// </devdoc>
360         public bool GetStandardValuesExclusive() {
361             return GetStandardValuesExclusive(null);
362         }
363
364         /// <devdoc>
365         ///    <para>Gets a value indicating whether the collection of standard values returned from
366         ///    <see cref='System.ComponentModel.TypeConverter.GetStandardValues'/> is an exclusive 
367         ///       list of possible values, using the specified context.</para>
368         /// </devdoc>
369         public virtual bool GetStandardValuesExclusive(ITypeDescriptorContext context) {
370             return false;
371         }
372
373         /// <devdoc>
374         ///    <para>
375         ///       Gets a value indicating whether this object supports a standard set of values
376         ///       that can be picked from a list.
377         ///    </para>
378         /// </devdoc>
379         public bool GetStandardValuesSupported() {
380             return GetStandardValuesSupported(null);
381         }
382
383         /// <devdoc>
384         ///    <para>Gets a value indicating
385         ///       whether this object
386         ///       supports a standard set of values that can be picked
387         ///       from a list using the specified context.</para>
388         /// </devdoc>
389         public virtual bool GetStandardValuesSupported(ITypeDescriptorContext context) {
390             return false;
391         }
392         
393         /// <devdoc>
394         ///    <para>
395         ///       Gets
396         ///       a value indicating whether the given value object is valid for this type.
397         ///    </para>
398         /// </devdoc>
399         public bool IsValid(object value) {
400             return IsValid(null, value);
401         }
402
403         /// <devdoc>
404         ///    <para>Gets
405         ///       a value indicating whether the given value object is valid for this type.</para>
406         /// </devdoc>
407         public virtual bool IsValid(ITypeDescriptorContext context, object value) {
408             if (UseCompatibleTypeConversion) {
409                 return true;
410             }
411
412             bool isValid = true;
413             try {
414                 // Because null doesn't have a type, so we couldn't pass this to CanConvertFrom.
415                 // Meanwhile, we couldn't silence null value here, such as type converter like
416                 // NullableConverter would consider null value as a valid value.
417                 if (value == null || CanConvertFrom(context, value.GetType())) {
418                     ConvertFrom(context, CultureInfo.InvariantCulture, value);
419                 }
420                 else {
421                     isValid = false;
422                 }
423             }
424             catch {
425                 isValid = false;
426             }
427
428             return isValid;
429         }
430         
431         /// <devdoc>
432         ///    <para>Sorts a collection of properties.</para>
433         /// </devdoc>
434         protected PropertyDescriptorCollection SortProperties(PropertyDescriptorCollection props, string[] names) {
435             props.Sort(names);
436             return props;
437         }
438
439         /// <devdoc>
440         ///    <para>
441         ///       An <see langword='abstract '/>
442         ///       class that provides
443         ///       properties for objects that do not have
444         ///       properties.
445         ///    </para>
446         /// </devdoc>
447         protected abstract class SimplePropertyDescriptor : PropertyDescriptor {
448             private Type   componentType;
449             private Type   propertyType;
450         
451
452             /// <devdoc>
453             ///    <para>
454             ///       Initializes a new instance of the <see cref='System.ComponentModel.TypeConverter.SimplePropertyDescriptor'/>
455             ///       class.
456             ///    </para>
457             /// </devdoc>
458             protected SimplePropertyDescriptor(Type componentType, string name, Type propertyType) : this(componentType, name, propertyType, new Attribute[0]) {
459             }
460             
461             /// <devdoc>
462             ///    <para>
463             ///       Initializes a new instance of the <see cref='System.ComponentModel.TypeConverter.SimplePropertyDescriptor'/> class.
464             ///    </para>
465             /// </devdoc>
466             protected SimplePropertyDescriptor(Type componentType, string name, Type propertyType, Attribute[] attributes) : base(name, attributes) {
467                 this.componentType = componentType;
468                 this.propertyType = propertyType;
469             }
470
471             /// <devdoc>
472             ///    <para>
473             ///       Gets the type of the component this property description
474             ///       is bound to.
475             ///    </para>
476             /// </devdoc>
477             public override Type ComponentType {
478                 get {
479                     return componentType;
480                 }
481             }
482                 
483             /// <devdoc>
484             ///    <para>
485             ///       Gets a
486             ///       value indicating whether this property is read-only.
487             ///    </para>
488             /// </devdoc>
489             public override bool IsReadOnly {
490                 get {
491                     return Attributes.Contains(ReadOnlyAttribute.Yes);
492                 }
493             }
494     
495             /// <devdoc>
496             ///    <para>
497             ///       Gets the type of the property.
498             ///    </para>
499             /// </devdoc>
500             public override Type PropertyType {
501                 get {
502                     return propertyType;
503                 }
504             }
505             
506             /// <devdoc>
507             ///    <para>Gets a value indicating whether resetting the component 
508             ///       will change the value of the component.</para>
509             /// </devdoc>
510             public override bool CanResetValue(object component) {
511                 DefaultValueAttribute attr = (DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)];
512                 if (attr == null) {
513                     return false;
514                 }
515                 return (attr.Value.Equals(GetValue(component)));
516             }
517             
518             /// <devdoc>
519             ///    <para>Resets the value for this property
520             ///       of the component.</para>
521             /// </devdoc>
522             public override void ResetValue(object component) {
523                 DefaultValueAttribute attr = (DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)];
524                 if (attr != null) {
525                     SetValue(component, attr.Value);
526                 }
527             }
528     
529             /// <devdoc>
530             ///    <para>Gets a value
531             ///       indicating whether the value of this property needs to be persisted.</para>
532             /// </devdoc>
533             public override bool ShouldSerializeValue(object component) {
534                 return false;
535             }
536         }
537         
538         /// <devdoc>
539         ///    <para>Represents a collection of values.</para>
540         /// </devdoc>
541         public class StandardValuesCollection : ICollection {
542             private ICollection values;
543             private Array       valueArray;
544             
545             /// <devdoc>
546             ///    <para>
547             ///       Initializes a new instance of the <see cref='System.ComponentModel.TypeConverter.StandardValuesCollection'/>
548             ///       class.
549             ///    </para>
550             /// </devdoc>
551             public StandardValuesCollection(ICollection values) {
552                 if (values == null) {
553                     values = new object[0];
554                 }
555                 
556                 Array a = values as Array;
557                 if (a != null) {
558                     valueArray = a;
559                 }
560                 
561                 this.values = values;
562             }
563             
564             /// <devdoc>
565             ///    <para>
566             ///       Gets the number of objects in the collection.
567             ///    </para>
568             /// </devdoc>
569             public int Count {
570                 get {
571                     if (valueArray != null) {
572                         return valueArray.Length;
573                     }
574                     else {
575                         return values.Count;
576                     }
577                 }
578             }
579             
580             /// <devdoc>
581             ///    <para>Gets the object at the specified index number.</para>
582             /// </devdoc>
583             public object this[int index] {
584                 get {
585                     if (valueArray != null) {
586                         return valueArray.GetValue(index);
587                     }
588                     IList list = values as IList;
589                     if (list != null) {
590                         return list[index];
591                     }
592                     // No other choice but to enumerate the collection.
593                     //
594                     valueArray = new object[values.Count];
595                     values.CopyTo(valueArray, 0);
596                     return valueArray.GetValue(index);
597                 }
598             }
599
600             /// <devdoc>
601             ///    <para>Copies the contents of this collection to an array.</para>
602             /// </devdoc>
603             public void CopyTo(Array array, int index) {
604                 values.CopyTo(array, index);
605             }
606
607             /// <devdoc>
608             ///    <para>
609             ///       Gets an enumerator for this collection.
610             ///    </para>
611             /// </devdoc>
612             public IEnumerator GetEnumerator() {
613                 return values.GetEnumerator();
614             }
615             
616             /// <internalonly/>
617             /// <devdoc>
618             /// Retrieves the count of objects in the collection.
619             /// </devdoc>
620             int ICollection.Count {
621                 get {
622                     return Count;
623                 }
624             }
625
626             /// <internalonly/>
627             /// <devdoc>
628             /// Determines if this collection is synchronized.
629             /// The ValidatorCollection is not synchronized for
630             /// speed.  Also, since it is read-only, there is
631             /// no need to synchronize it.
632             /// </devdoc>
633             bool ICollection.IsSynchronized {
634                 get {
635                     return false;
636                 }
637             }
638
639             /// <internalonly/>
640             /// <devdoc>
641             /// Retrieves the synchronization root for this
642             /// collection.  Because we are not synchronized,
643             /// this returns null.
644             /// </devdoc>
645             object ICollection.SyncRoot {
646                 get {
647                     return null;
648                 }
649             }
650
651             /// <internalonly/>
652             /// <devdoc>
653             /// Copies the contents of this collection to an array.
654             /// </devdoc>
655             void ICollection.CopyTo(Array array, int index) {
656                 CopyTo(array, index);
657             }
658
659             /// <internalonly/>
660             /// <devdoc>
661             /// Retrieves a new enumerator that can be used to
662             /// iterate over the values in this collection.
663             /// </devdoc>
664             IEnumerator IEnumerable.GetEnumerator() {
665                 return GetEnumerator();
666             }
667         }
668     }
669 }
670