c7002e5c8eeecc6a33cfee6ba0e17835af563f41
[mono.git] / mcs / class / System.Xaml / System.Xaml / TypeExtensionMethods.cs
1 //
2 // Copyright (C) 2010 Novell Inc. http://novell.com
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 using System;
24 using System.Collections;
25 using System.Collections.Generic;
26 using System.ComponentModel;
27 using System.Globalization;
28 using System.Linq;
29 using System.Reflection;
30 using System.Windows.Markup;
31 using System.Xaml.Schema;
32
33 namespace System.Xaml
34 {
35         static class TypeExtensionMethods
36         {
37                 #region inheritance search and custom attribute provision
38
39                 public static T GetCustomAttribute<T> (this ICustomAttributeProvider type, bool inherit) where T : Attribute
40                 {
41                         foreach (var a in type.GetCustomAttributes (typeof (T), inherit))
42                                 return (T) (object) a;
43                         return null;
44                 }
45
46                 public static T GetCustomAttribute<T> (this XamlType type) where T : Attribute
47                 {
48                         if (type.UnderlyingType == null)
49                                 return null;
50
51                         T ret = type.GetCustomAttributeProvider ().GetCustomAttribute<T> (true);
52                         if (ret != null)
53                                 return ret;
54                         if (type.BaseType != null)
55                                 return type.BaseType.GetCustomAttribute<T> ();
56                         return null;
57                 }
58
59                 public static bool ImplementsAnyInterfacesOf (this Type type, params Type [] definitions)
60                 {
61                         return definitions.Any (t => ImplementsInterface (type, t));
62                 }
63
64                 public static bool ImplementsInterface (this Type type, Type definition)
65                 {
66                         if (type == null)
67                                 throw new ArgumentNullException ("type");
68                         if (definition == null)
69                                 throw new ArgumentNullException ("definition");
70                         if (type == definition)
71                                 return true;
72
73                         if (type.IsGenericType && type.GetGenericTypeDefinition () == definition)
74                                 return true;
75
76                         foreach (var iface in type.GetInterfaces ())
77                                 if (iface == definition || (iface.IsGenericType && iface.GetGenericTypeDefinition () == definition))
78                                         return true;
79                         return false;
80                 }
81                 
82                 #endregion
83                 
84                 #region type conversion and member value retrieval
85                 
86                 static readonly NullExtension null_value = new NullExtension ();
87
88                 public static object GetExtensionWrapped (object o)
89                 {
90                         // FIXME: should this manually checked, or is there any way to automate it?
91                         // Also XamlSchemaContext might be involved but this method signature does not take it consideration.
92                         if (o == null)
93                                 return null_value;
94                         if (o is Array)
95                                 return new ArrayExtension ((Array) o);
96                         if (o is Type)
97                                 return new TypeExtension ((Type) o);
98                         return o;
99                 }
100                 
101                 public static string GetStringValue (XamlType xt, XamlMember xm, object obj, IValueSerializerContext vsctx)
102                 {
103                         if (obj == null)
104                                 return String.Empty;
105                         if (obj is Type)
106                                 return new XamlTypeName (xt.SchemaContext.GetXamlType ((Type) obj)).ToString (vsctx != null ? vsctx.GetService (typeof (INamespacePrefixLookup)) as INamespacePrefixLookup : null);
107
108                         var vs = (xm != null ? xm.ValueSerializer : null) ?? xt.ValueSerializer;
109                         if (vs != null)
110                                 return vs.ConverterInstance.ConvertToString (obj, vsctx);
111
112                         // FIXME: does this make sense?
113                         var vc = (xm != null ? xm.TypeConverter : null) ?? xt.TypeConverter;
114                         var tc = vc != null ? vc.ConverterInstance : null;
115                         if (tc != null && typeof (string) != null && tc.CanConvertTo (vsctx, typeof (string)))
116                                 return (string) tc.ConvertTo (vsctx, CultureInfo.InvariantCulture, obj, typeof (string));
117                         if (obj is string || obj == null)
118                                 return (string) obj;
119                         throw new InvalidCastException (String.Format ("Cannot cast object '{0}' to string", obj.GetType ()));
120                 }
121                 
122                 public static TypeConverter GetTypeConverter (this Type type)
123                 {
124                         return TypeDescriptor.GetConverter (type);
125                 }
126                 
127                 // FIXME: I want this to cover all the existing types and make it valid in both NET_2_1 and !NET_2_1.
128                 class ConvertibleTypeConverter<T> : TypeConverter
129                 {
130                         Type type;
131                         public ConvertibleTypeConverter ()
132                         {
133                                 this.type = typeof (T);
134                         }
135                         public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
136                         {
137                                 return sourceType == typeof (string);
138                         }
139                         public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
140                         {
141                                 return destinationType == typeof (string);
142                         }
143                         public override object ConvertFrom (ITypeDescriptorContext context, CultureInfo culture, object value)
144                         {
145                                 if (type == typeof (DateTime))
146                                         return System.Xml.XmlConvert.ToDateTime ((string) value, System.Xml.XmlDateTimeSerializationMode.Unspecified);
147                                 return ((IConvertible) value).ToType (type, CultureInfo.InvariantCulture);
148                         }
149                         public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
150                         {
151                                 if (value is DateTime)
152                                         return System.Xml.XmlConvert.ToString ((DateTime) value);
153                                 return ((IConvertible) value).ToType (destinationType, CultureInfo.InvariantCulture);
154                         }
155                 }
156
157                 #endregion
158
159                 public static bool IsContentValue (this XamlMember member, IValueSerializerContext vsctx)
160                 {
161                         if (member == XamlLanguage.Initialization)
162                                 return true;
163                         if (member == XamlLanguage.PositionalParameters || member == XamlLanguage.Arguments)
164                                 return false; // it's up to the argument (no need to check them though, as IList<object> is not of value)
165                         if (member.TypeConverter != null && member.TypeConverter.ConverterInstance != null && member.TypeConverter.ConverterInstance.CanConvertTo (vsctx, typeof (string)))
166                                 return true;
167                         return IsContentValue (member.Type,vsctx);
168                 }
169
170                 public static bool IsContentValue (this XamlType type, IValueSerializerContext vsctx)
171                 {
172                         if (type.TypeConverter != null && type.TypeConverter.ConverterInstance != null && type.TypeConverter.ConverterInstance.CanConvertTo (vsctx, typeof (string)))
173                                 return true;
174                         return false;
175                 }
176
177                 public static bool ListEquals (this IList<XamlType> a1, IList<XamlType> a2)
178                 {
179                         if (a1 == null || a1.Count == 0)
180                                 return a2 == null || a2.Count == 0;
181                         if (a2 == null || a2.Count == 0)
182                                 return false;
183                         if (a1.Count != a2.Count)
184                                 return false;
185                         for (int i = 0; i < a1.Count; i++)
186                                 if (a1 [i] != a2 [i])
187                                         return false;
188                         return true;
189                 }
190
191                 public static bool HasPositionalParameters (this XamlType type, IValueSerializerContext vsctx)
192                 {
193                         // FIXME: find out why only TypeExtension and StaticExtension yield this directive. Seealso XamlObjectReaderTest.Read_CustomMarkupExtension*()
194                         return  type == XamlLanguage.Type ||
195                                 type == XamlLanguage.Static ||
196                                 ExaminePositionalParametersApplicable (type, vsctx) && type.ConstructionRequiresArguments;
197                 }
198                 
199                 static bool ExaminePositionalParametersApplicable (this XamlType type, IValueSerializerContext vsctx)
200                 {
201                         if (!type.IsMarkupExtension || type.UnderlyingType == null)
202                                 return false;
203
204                         var args = type.GetSortedConstructorArguments ();
205                         if (args == null)
206                                 return false;
207
208                         foreach (var arg in args)
209                                 if (arg.Type != null && !arg.Type.IsContentValue (vsctx))
210                                         return false;
211
212                         Type [] argTypes = (from arg in args select arg.Type.UnderlyingType).ToArray ();
213                         if (argTypes.Any (at => at == null))
214                                 return false;
215                         var ci = type.UnderlyingType.GetConstructor (argTypes);
216                         return ci != null;
217                 }
218                 
219                 public static IEnumerable<XamlMember> GetConstructorArguments (this XamlType type)
220                 {
221                         return type.GetAllMembers ().Where (m => m.UnderlyingMember != null && m.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false) != null);
222                 }
223
224                 public static IEnumerable<XamlMember> GetSortedConstructorArguments (this XamlType type)
225                 {
226                         var args = type.GetConstructorArguments ().ToArray ();
227                         foreach (var ci in type.UnderlyingType.GetConstructors ().Where (c => c.GetParameters ().Length == args.Length)) {
228                                 var pis = ci.GetParameters ();
229                                 if (args.Length != pis.Length)
230                                         continue;
231                                 bool mismatch = false;
232                                 foreach (var pi in pis)
233                                 for (int i = 0; i < args.Length; i++)
234                                         if (!args.Any (a => a.ConstructorArgumentName () == pi.Name))
235                                                 mismatch = true;
236                                 if (mismatch)
237                                         continue;
238                                 return args.OrderBy (c => pis.FindParameterWithName (c.ConstructorArgumentName ()).Position);
239                         }
240                         return null;
241                 }
242
243                 static ParameterInfo FindParameterWithName (this IEnumerable<ParameterInfo> pis, string name)
244                 {
245                         return pis.FirstOrDefault (pi => pi.Name == name);
246                 }
247
248                 public static string ConstructorArgumentName (this XamlMember xm)
249                 {
250                         var caa = xm.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false);
251                         return caa.ArgumentName;
252                 }
253                 
254
255                 internal static int CompareMembers (XamlMember m1, XamlMember m2)
256                 {
257                         // ConstructorArguments and PositionalParameters go first.
258                         if (m1 == XamlLanguage.PositionalParameters)
259                                 return -1;
260                         if (m2 == XamlLanguage.PositionalParameters)
261                                 return 1;
262                         if (m1.IsConstructorArgument ()) {
263                                 if (!m2.IsConstructorArgument ())
264                                         return -1;
265                         }
266                         else if (m2.IsConstructorArgument ())
267                                 return 1;
268
269                         // ContentProperty is returned at last.
270                         if (m1.DeclaringType != null && m1.DeclaringType.ContentProperty == m1)
271                                 return 1;
272                         if (m2.DeclaringType != null && m2.DeclaringType.ContentProperty == m2)
273                                 return -1;
274
275                         // then, compare names.
276                         return String.CompareOrdinal (m1.Name, m2.Name);
277                 }
278
279                 internal static bool IsConstructorArgument (this XamlMember xm)
280                 {
281                         var ap = xm.GetCustomAttributeProvider ();
282                         return ap != null && ap.GetCustomAttributes (typeof (ConstructorArgumentAttribute), false).Length > 0;
283                 }
284
285                 internal static string GetInternalXmlName (this XamlMember xm)
286                 {
287                         return xm.IsAttachable ? String.Concat (xm.DeclaringType.GetInternalXmlName (), ".", xm.Name) : xm.Name;
288                 }
289
290 #if DOTNET
291                 internal static ICustomAttributeProvider GetCustomAttributeProvider (this XamlType type)
292                 {
293                         return type.UnderlyingType;
294                 }
295                 
296                 internal static ICustomAttributeProvider GetCustomAttributeProvider (this XamlMember member)
297                 {
298                         return member.UnderlyingMember;
299                 }
300 #endif
301         }
302 }