2 // Copyright (C) 2010 Novell Inc. http://novell.com
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
24 using System.Collections;
25 using System.Collections.Generic;
26 using System.ComponentModel;
27 using System.Globalization;
29 using System.Reflection;
30 using System.Windows.Markup;
31 using System.Xaml.Schema;
35 static class TypeExtensionMethods
37 #region inheritance search and custom attribute provision
39 public static T GetCustomAttribute<T> (this ICustomAttributeProvider type, bool inherit) where T : Attribute
41 foreach (var a in type.GetCustomAttributes (typeof (T), inherit))
42 return (T) (object) a;
46 public static T GetCustomAttribute<T> (this XamlType type) where T : Attribute
48 if (type.UnderlyingType == null)
51 T ret = type.GetCustomAttributeProvider ().GetCustomAttribute<T> (true);
54 if (type.BaseType != null)
55 return type.BaseType.GetCustomAttribute<T> ();
59 public static bool ImplementsAnyInterfacesOf (this Type type, params Type [] definitions)
61 return definitions.Any (t => ImplementsInterface (type, t));
64 public static bool ImplementsInterface (this Type type, Type definition)
67 throw new ArgumentNullException ("type");
68 if (definition == null)
69 throw new ArgumentNullException ("definition");
70 if (type == definition)
73 foreach (var iface in type.GetInterfaces ())
74 if (iface == definition || (iface.IsGenericType && iface.GetGenericTypeDefinition () == definition))
81 #region type conversion and member value retrieval
83 public static string GetStringValue (XamlType xt, XamlMember xm, object obj, IValueSerializerContext vsctx)
88 return new XamlTypeName (xt.SchemaContext.GetXamlType ((Type) obj)).ToString (vsctx != null ? vsctx.GetService (typeof (INamespacePrefixLookup)) as INamespacePrefixLookup : null);
90 var vs = (xm != null ? xm.ValueSerializer : null) ?? xt.ValueSerializer;
92 return vs.ConverterInstance.ConvertToString (obj, vsctx);
94 // FIXME: does this make sense?
95 var vc = (xm != null ? xm.TypeConverter : null) ?? xt.TypeConverter;
96 var tc = vc != null ? vc.ConverterInstance : null;
97 if (tc != null && typeof (string) != null && tc.CanConvertTo (vsctx, typeof (string)))
98 return tc.ConvertToInvariantString (vsctx, obj);
99 if (obj is string || obj == null)
101 throw new InvalidCastException (String.Format ("Cannot cast object '{0}' to string", obj.GetType ()));
106 public static bool IsContentValue (this XamlMember member, IValueSerializerContext vsctx)
108 if (member == XamlLanguage.Initialization)
110 if (member == XamlLanguage.PositionalParameters)
112 if (member.TypeConverter != null && member.TypeConverter.ConverterInstance != null && member.TypeConverter.ConverterInstance.CanConvertTo (vsctx, typeof (string)))
114 return IsContentValue (member.Type,vsctx);
117 public static bool IsContentValue (this XamlType type, IValueSerializerContext vsctx)
119 var t = type.UnderlyingType;
120 if (type.TypeConverter != null && type.TypeConverter.ConverterInstance != null && type.TypeConverter.ConverterInstance.CanConvertTo (vsctx, typeof (string)))
125 public static bool ListEquals (this IList<XamlType> a1, IList<XamlType> a2)
127 if (a1 == null || a1.Count == 0)
128 return a2 == null || a2.Count == 0;
129 if (a2 == null || a2.Count == 0)
131 if (a1.Count != a2.Count)
133 for (int i = 0; i < a1.Count; i++)
134 if (a1 [i] != a2 [i])
139 public static bool HasPositionalParameters (this XamlType type, IValueSerializerContext vsctx)
141 // FIXME: find out why only TypeExtension and StaticExtension yield this directive. Seealso XamlObjectReaderTest.Read_CustomMarkupExtension*()
142 return type == XamlLanguage.Type ||
143 type == XamlLanguage.Static ||
144 ExaminePositionalParametersApplicable (type, vsctx) && type.ConstructionRequiresArguments;
147 static bool ExaminePositionalParametersApplicable (this XamlType type, IValueSerializerContext vsctx)
149 if (!type.IsMarkupExtension || type.UnderlyingType == null)
152 var args = type.GetSortedConstructorArguments ();
156 foreach (var arg in args)
157 if (arg.Type != null && !arg.Type.IsContentValue (vsctx))
160 Type [] argTypes = (from arg in args select arg.Type.UnderlyingType).ToArray ();
161 if (argTypes.Any (at => at == null))
163 var ci = type.UnderlyingType.GetConstructor (argTypes);
167 public static IEnumerable<XamlMember> GetConstructorArguments (this XamlType type)
169 return type.GetAllMembers ().Where (m => m.UnderlyingMember != null && m.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false) != null);
172 public static IEnumerable<XamlMember> GetSortedConstructorArguments (this XamlType type)
174 var args = type.GetConstructorArguments ().ToArray ();
175 foreach (var ci in type.UnderlyingType.GetConstructors ().Where (c => c.GetParameters ().Length == args.Length)) {
176 var pis = ci.GetParameters ();
177 if (args.Length != pis.Length)
179 bool mismatch = false;
180 foreach (var pi in pis)
181 for (int i = 0; i < args.Length; i++)
182 if (!args.Any (a => a.ConstructorArgumentName () == pi.Name))
186 return args.OrderBy (c => pis.FindParameterWithName (c.ConstructorArgumentName ()).Position);
191 static ParameterInfo FindParameterWithName (this IEnumerable<ParameterInfo> pis, string name)
193 return pis.FirstOrDefault (pi => pi.Name == name);
196 public static string ConstructorArgumentName (this XamlMember xm)
198 var caa = xm.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false);
199 return caa.ArgumentName;
203 internal static int CompareMembers (XamlMember m1, XamlMember m2)
205 // ConstructorArguments and PositionalParameters go first.
206 if (m1 == XamlLanguage.PositionalParameters)
208 if (m2 == XamlLanguage.PositionalParameters)
210 if (m1.IsConstructorArgument ()) {
211 if (!m2.IsConstructorArgument ())
214 else if (m2.IsConstructorArgument ())
217 // ContentProperty is returned at last.
218 if (m1.DeclaringType != null && m1.DeclaringType.ContentProperty == m1)
220 if (m2.DeclaringType != null && m2.DeclaringType.ContentProperty == m2)
223 // compare collection kind
226 int coll1 = t1.IsDictionary ? 3 : t1.IsCollection ? 2 : t1.IsArray ? 1 : 0;
227 int coll2 = t2.IsDictionary ? 3 : t2.IsCollection ? 2 : t2.IsArray ? 1 : 0;
229 return coll2 - coll1;
231 // then, compare names.
232 return String.CompareOrdinal (m1.Name, m2.Name);
235 internal static bool IsConstructorArgument (this XamlMember xm)
237 var ap = xm.GetCustomAttributeProvider ();
238 return ap != null && ap.GetCustomAttributes (typeof (ConstructorArgumentAttribute), false).Length > 0;
242 internal static ICustomAttributeProvider GetCustomAttributeProvider (this XamlType type)
244 return type.UnderlyingType;
247 internal static ICustomAttributeProvider GetCustomAttributeProvider (this XamlMember member)
249 return member.UnderlyingMember;