Do not retrive properties with get parameters (indexers).
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlType.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.Linq;
28 using System.Reflection;
29 using System.Windows.Markup;
30 using System.Xaml.Schema;
31
32 namespace System.Xaml
33 {
34         public class XamlType : IEquatable<XamlType>
35         {
36                 public XamlType (Type underlyingType, XamlSchemaContext schemaContext)
37                         : this (underlyingType, schemaContext, null)
38                 {
39                 }
40
41 //              static readonly Type [] predefined_types = {
42 //                              typeof (XData), typeof (Uri), typeof (TimeSpan), typeof (PropertyDefinition), typeof (MemberDefinition), typeof (Reference)
43 //                      };
44
45                 public XamlType (Type underlyingType, XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
46                         : this (schemaContext, invoker)
47                 {
48                         if (underlyingType == null)
49                                 throw new ArgumentNullException ("underlyingType");
50                         type = underlyingType;
51                         underlying_type = type;
52
53                         XamlType xt;
54                         if (XamlLanguage.InitializingTypes) {
55                                 // These are special. Only XamlLanguage members are with shorthand name.
56                                 if (type == typeof (PropertyDefinition))
57                                         Name = "Property";
58                                 else if (type == typeof (MemberDefinition))
59                                         Name = "Member";
60                                 else
61                                         Name = GetXamlName (type);
62                                 PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
63                         } else if ((xt = XamlLanguage.AllTypes.FirstOrDefault (t => t.UnderlyingType == type)) != null) {
64                                 Name = xt.Name;
65                                 PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
66                         } else {
67                                 Name = GetXamlName (type);
68                                 PreferredXamlNamespace = String.Format ("clr-namespace:{0};assembly={1}", type.Namespace, type.Assembly.GetName ().Name);
69                         }
70                         if (type.IsGenericType) {
71                                 TypeArguments = new List<XamlType> ();
72                                 foreach (var gta in type.GetGenericArguments ())
73                                         TypeArguments.Add (schemaContext.GetXamlType (gta));
74                         }
75                 }
76
77                 public XamlType (string unknownTypeNamespace, string unknownTypeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
78                         : this (schemaContext, null)
79                 {
80                         if (unknownTypeNamespace == null)
81                                 throw new ArgumentNullException ("unknownTypeNamespace");
82                         if (unknownTypeName == null)
83                                 throw new ArgumentNullException ("unknownTypeName");
84                         if (schemaContext == null)
85                                 throw new ArgumentNullException ("schemaContext");
86
87                         type = typeof (object);
88                         Name = unknownTypeName;
89                         PreferredXamlNamespace = unknownTypeNamespace;
90                         TypeArguments = typeArguments != null && typeArguments.Count == 0 ? null : typeArguments;
91                         explicit_ns = unknownTypeNamespace;
92                 }
93
94                 protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
95                         : this (String.Empty, typeName, typeArguments, schemaContext)
96                 {
97                 }
98
99                 XamlType (XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
100                 {
101                         if (schemaContext == null)
102                                 throw new ArgumentNullException ("schemaContext");
103                         SchemaContext = schemaContext;
104                         this.invoker = invoker ?? new XamlTypeInvoker (this);
105                 }
106
107                 Type type, underlying_type;
108
109                 string explicit_ns;
110
111                 // populated properties
112                 XamlType base_type;
113                 XamlTypeInvoker invoker;
114
115                 internal EventHandler<XamlSetMarkupExtensionEventArgs> SetMarkupExtensionHandler {
116                         get { return LookupSetMarkupExtensionHandler (); }
117                 }
118
119                 internal EventHandler<XamlSetTypeConverterEventArgs> SetTypeConverterHandler {
120                         get { return LookupSetTypeConverterHandler (); }
121                 }
122
123                 public IList<XamlType> AllowedContentTypes {
124                         get { return LookupAllowedContentTypes (); }
125                 }
126
127                 public XamlType BaseType {
128                         get { return LookupBaseType (); }
129                 }
130
131                 public bool ConstructionRequiresArguments {
132                         get { return LookupConstructionRequiresArguments (); }
133                 }
134
135                 public XamlMember ContentProperty {
136                         get { return LookupContentProperty (); }
137                 }
138
139                 public IList<XamlType> ContentWrappers {
140                         get { return LookupContentWrappers (); }
141                 }
142
143                 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
144                         get { return LookupDeferringLoader (); }
145                 }
146
147                 public XamlTypeInvoker Invoker {
148                         get { return LookupInvoker (); }
149                 }
150
151                 public bool IsAmbient {
152                         get { return LookupIsAmbient (); }
153                 }
154
155                 public bool IsArray {
156                         get { return LookupCollectionKind () == XamlCollectionKind.Array; }
157                 }
158
159                 // it somehow treats array as not a collection...
160                 public bool IsCollection {
161                         get { return LookupCollectionKind () == XamlCollectionKind.Collection; }
162                 }
163
164                 public bool IsConstructible {
165                         get { return LookupIsConstructible (); }
166                 }
167
168                 public bool IsDictionary {
169                         get { return LookupCollectionKind () == XamlCollectionKind.Dictionary; }
170                 }
171
172                 public bool IsGeneric {
173                         get { return type.IsGenericType; }
174                 }
175
176                 public bool IsMarkupExtension {
177                         get { return LookupIsMarkupExtension (); }
178                 }
179                 public bool IsNameScope {
180                         get { return LookupIsNameScope (); }
181                 }
182                 public bool IsNameValid {
183                         get { return XamlLanguage.IsValidXamlName (Name); }
184                 }
185
186                 public bool IsNullable {
187                         get { return LookupIsNullable (); }
188                 }
189
190                 public bool IsPublic {
191                         get { return LookupIsPublic (); }
192                 }
193
194                 public bool IsUnknown {
195                         get { return LookupIsUnknown (); }
196                 }
197
198                 public bool IsUsableDuringInitialization {
199                         get { return LookupUsableDuringInitialization (); }
200                 }
201
202                 public bool IsWhitespaceSignificantCollection {
203                         get { return LookupIsWhitespaceSignificantCollection (); }
204                 }
205
206                 public bool IsXData {
207                         get { return LookupIsXData (); }
208                 }
209
210                 public XamlType ItemType {
211                         get { return LookupItemType (); }
212                 }
213
214                 public XamlType KeyType {
215                         get { return LookupKeyType (); }
216                 }
217
218                 public XamlType MarkupExtensionReturnType {
219                         get { return LookupMarkupExtensionReturnType (); }
220                 }
221
222                 public string Name { get; private set; }
223
224                 public string PreferredXamlNamespace { get; private set; }
225
226                 public XamlSchemaContext SchemaContext { get; private set; }
227
228                 public bool TrimSurroundingWhitespace {
229                         get { return LookupTrimSurroundingWhitespace (); }
230                 }
231
232                 public IList<XamlType> TypeArguments { get; private set; }
233
234                 public XamlValueConverter<TypeConverter> TypeConverter {
235                         get { return LookupTypeConverter (); }
236                 }
237
238                 public Type UnderlyingType {
239                         get { return LookupUnderlyingType (); }
240                 }
241
242                 public XamlValueConverter<ValueSerializer> ValueSerializer {
243                         get { return LookupValueSerializer (); }
244                 }
245
246                 internal string GetInternalXmlName ()
247                 {
248                         if (IsMarkupExtension && Name.EndsWith ("Extension", StringComparison.Ordinal))
249                                 return Name.Substring (0, Name.Length - 9);
250                         var stn = XamlLanguage.SpecialNames.FirstOrDefault (s => s.Type == this);
251                         return stn != null ? stn.Name : Name;
252                 }
253
254                 public static bool operator == (XamlType left, XamlType right)
255                 {
256                         return IsNull (left) ? IsNull (right) : left.Equals (right);
257                 }
258
259                 static bool IsNull (XamlType a)
260                 {
261                         return Object.ReferenceEquals (a, null);
262                 }
263
264                 public static bool operator != (XamlType left, XamlType right)
265                 {
266                         return !(left == right);
267                 }
268                 
269                 public bool Equals (XamlType other)
270                 {
271                         // It does not compare XamlSchemaContext.
272                         return !IsNull (other) &&
273                                 UnderlyingType == other.UnderlyingType &&
274                                 Name == other.Name &&
275                                 PreferredXamlNamespace == other.PreferredXamlNamespace && TypeArguments.ListEquals (other.TypeArguments);
276                 }
277
278                 public override bool Equals (object obj)
279                 {
280                         var a = obj as XamlType;
281                         return Equals (a);
282                 }
283                 
284                 public override int GetHashCode ()
285                 {
286                         if (UnderlyingType != null)
287                                 return UnderlyingType.GetHashCode ();
288                         int x = Name.GetHashCode () << 7 + PreferredXamlNamespace.GetHashCode ();
289                         if (TypeArguments != null)
290                                 foreach (var t in TypeArguments)
291                                         x = t.GetHashCode () + x << 5;
292                         return x;
293                 }
294
295                 public override string ToString ()
296                 {
297                         return new XamlTypeName (this).ToString ();
298                         //return String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
299                 }
300
301                 public virtual bool CanAssignTo (XamlType xamlType)
302                 {
303                         if (this.UnderlyingType == null)
304                                 return true;
305                         var ut = xamlType.UnderlyingType ?? typeof (object);
306                         return ut.IsAssignableFrom (UnderlyingType);
307                 }
308
309                 public XamlMember GetAliasedProperty (XamlDirective directive)
310                 {
311                         return LookupAliasedProperty (directive);
312                 }
313
314                 public ICollection<XamlMember> GetAllAttachableMembers ()
315                 {
316                         return new List<XamlMember> (LookupAllAttachableMembers ());
317                 }
318
319                 public ICollection<XamlMember> GetAllMembers ()
320                 {
321                         return new List<XamlMember> (LookupAllMembers ());
322                 }
323
324                 public XamlMember GetAttachableMember (string name)
325                 {
326                         return LookupAttachableMember (name);
327                 }
328
329                 public XamlMember GetMember (string name)
330                 {
331                         return LookupMember (name, true);
332                 }
333
334                 public IList<XamlType> GetPositionalParameters (int parameterCount)
335                 {
336                         return LookupPositionalParameters (parameterCount);
337                 }
338
339                 public virtual IList<string> GetXamlNamespaces ()
340                 {
341                         throw new NotImplementedException ();
342                         /* this does not work like documented!
343                         if (explicit_ns != null)
344                                 return new string [] {explicit_ns};
345                         var l = SchemaContext.GetAllXamlNamespaces ();
346                         if (l != null)
347                                 return new List<string> (l);
348                         return new string [] {String.Empty};
349                         */
350                 }
351
352                 // lookups
353
354                 protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
355                 {
356                         if (directive == XamlLanguage.Key) {
357                                 var a = this.GetCustomAttribute<DictionaryKeyPropertyAttribute> ();
358                                 return a != null ? GetMember (a.Name) : null;
359                         }
360                         if (directive == XamlLanguage.Name) {
361                                 var a = this.GetCustomAttribute<RuntimeNamePropertyAttribute> ();
362                                 return a != null ? GetMember (a.Name) : null;
363                         }
364                         if (directive == XamlLanguage.Uid) {
365                                 var a = this.GetCustomAttribute<UidPropertyAttribute> ();
366                                 return a != null ? GetMember (a.Name) : null;
367                         }
368                         if (directive == XamlLanguage.Lang) {
369                                 var a = this.GetCustomAttribute<XmlLangPropertyAttribute> ();
370                                 return a != null ? GetMember (a.Name) : null;
371                         }
372                         return null;
373                 }
374
375                 protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
376                 {
377                         if (UnderlyingType == null)
378                                 return BaseType != null ? BaseType.GetAllAttachableMembers () : null;
379                         return DoLookupAllAttachableMembers ();
380                 }
381
382                 IEnumerable<XamlMember> DoLookupAllAttachableMembers ()
383                 {
384                         yield break; // FIXME: what to return here?
385                 }
386
387                 static readonly XamlMember [] empty_array = new XamlMember [0];
388
389                 protected virtual IEnumerable<XamlMember> LookupAllMembers ()
390                 {
391                         if (UnderlyingType == null)
392                                 return BaseType != null ? BaseType.GetAllMembers () : empty_array;
393                         if (all_members_cache == null) {
394                                 all_members_cache = new List<XamlMember> (DoLookupAllMembers ());
395                                 all_members_cache.Sort (TypeExtensionMethods.CompareMembers);
396                         }
397                         return all_members_cache;
398                 }
399                 
400                 List<XamlMember> all_members_cache;
401
402                 IEnumerable<XamlMember> DoLookupAllMembers ()
403                 {
404                         // This is a hack that is likely required due to internal implementation difference in System.Uri. Our Uri has two readonly collection properties
405                         if (this == XamlLanguage.Uri)
406                                 yield break;
407
408                         var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
409
410                         foreach (var pi in UnderlyingType.GetProperties (bf))
411                                 if (pi.CanRead && (pi.CanWrite || IsCollectionType (pi.PropertyType)) && pi.GetIndexParameters ().Length == 0)
412                                         yield return new XamlMember (pi, SchemaContext);
413                         foreach (var ei in UnderlyingType.GetEvents (bf))
414                                 yield return new XamlMember (ei, SchemaContext);
415                 }
416                 
417                 static bool IsPublicAccessor (MethodInfo mi)
418                 {
419                         return mi != null && mi.IsPublic;
420                 }
421
422                 bool IsCollectionType (Type type)
423                 {
424                         if (type == null)
425                                 return false;
426                         var xt = SchemaContext.GetXamlType (type);
427                         return xt.LookupCollectionKind () != XamlCollectionKind.None;
428                 }
429
430                 protected virtual IList<XamlType> LookupAllowedContentTypes ()
431                 {
432                         // the actual implementation is very different from what is documented :(
433                         return null;
434
435                         /*
436                         var l = new List<XamlType> ();
437                         if (ContentWrappers != null)
438                                 l.AddRange (ContentWrappers);
439                         if (ContentProperty != null)
440                                 l.Add (ContentProperty.Type);
441                         if (ItemType != null)
442                                 l.Add (ItemType);
443                         return l.Count > 0 ? l : null;
444                         */
445                 }
446
447                 protected virtual XamlMember LookupAttachableMember (string name)
448                 {
449                         throw new NotImplementedException ();
450                 }
451
452                 [MonoTODO]
453                 protected virtual XamlType LookupBaseType ()
454                 {
455                         if (base_type == null) {
456                                 if (UnderlyingType == null)
457                                         // FIXME: probably something advanced is needed here.
458                                         base_type = new XamlType (typeof (object), SchemaContext, Invoker);
459                                 else
460                                         base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
461                         }
462                         return base_type;
463                 }
464
465                 // This implementation is not verified. (No place to use.)
466                 protected virtual XamlCollectionKind LookupCollectionKind ()
467                 {
468                         if (UnderlyingType == null)
469                                 return BaseType != null ? BaseType.LookupCollectionKind () : XamlCollectionKind.None;
470                         if (type.IsArray)
471                                 return XamlCollectionKind.Array;
472
473                         if (type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)))
474                                 return XamlCollectionKind.Dictionary;
475
476                         if (type.ImplementsAnyInterfacesOf (typeof (IList), typeof (ICollection<>)))
477                                 return XamlCollectionKind.Collection;
478
479                         return XamlCollectionKind.None;
480                 }
481
482                 protected virtual bool LookupConstructionRequiresArguments ()
483                 {
484                         if (UnderlyingType == null)
485                                 return false;
486
487                         // not sure if it is required, but MemberDefinition return true while they are abstract and it makes no sense.
488                         if (UnderlyingType.IsAbstract)
489                                 return true;
490
491                         // FIXME: probably some primitive types are treated as special.
492                         switch (Type.GetTypeCode (UnderlyingType)) {
493                         case TypeCode.String:
494                                 return true;
495                         case TypeCode.Object:
496                                 if (UnderlyingType == typeof (TimeSpan))
497                                         return false;
498                                 break;
499                         default:
500                                 return false;
501                         }
502
503                         return UnderlyingType.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) == null;
504                 }
505
506                 protected virtual XamlMember LookupContentProperty ()
507                 {
508                         var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
509                         return a != null && a.Name != null ? GetMember (a.Name) : null;
510                 }
511
512                 protected virtual IList<XamlType> LookupContentWrappers ()
513                 {
514                         if (GetCustomAttributeProvider () == null)
515                                 return null;
516
517                         var arr = GetCustomAttributeProvider ().GetCustomAttributes (typeof (ContentWrapperAttribute), false);
518                         if (arr == null || arr.Length == 0)
519                                 return null;
520                         var l = new XamlType [arr.Length];
521                         for (int i = 0; i < l.Length; i++) 
522                                 l [i] = SchemaContext.GetXamlType (((ContentWrapperAttribute) arr [i]).ContentWrapper);
523                         return l;
524                 }
525
526                 internal ICustomAttributeProvider GetCustomAttributeProvider ()
527                 {
528                         return LookupCustomAttributeProvider ();
529                 }
530
531                 protected internal virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
532                 {
533                         return UnderlyingType;
534                 }
535                 
536                 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
537                 {
538                         throw new NotImplementedException ();
539                 }
540
541                 protected virtual XamlTypeInvoker LookupInvoker ()
542                 {
543                         return invoker;
544                 }
545
546                 protected virtual bool LookupIsAmbient ()
547                 {
548                         return this.GetCustomAttribute<AmbientAttribute> () != null;
549                 }
550
551                 // It is documented as if it were to reflect spec. section 5.2,
552                 // but the actual behavior shows it is *totally* wrong.
553                 // Here I have implemented this based on the nunit test results. sigh.
554                 protected virtual bool LookupIsConstructible ()
555                 {
556                         if (UnderlyingType == null)
557                                 return true;
558                         if (IsMarkupExtension)
559                                 return true;
560                         if (UnderlyingType.IsAbstract)
561                                 return false;
562                         if (!IsNameValid)
563                                 return false;
564                         return true;
565                 }
566
567                 protected virtual bool LookupIsMarkupExtension ()
568                 {
569                         return typeof (MarkupExtension).IsAssignableFrom (UnderlyingType);
570                 }
571
572                 protected virtual bool LookupIsNameScope ()
573                 {
574                         return typeof (INameScope).IsAssignableFrom (UnderlyingType);
575                 }
576
577                 protected virtual bool LookupIsNullable ()
578                 {
579                         return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
580                 }
581
582                 protected virtual bool LookupIsPublic ()
583                 {
584                         return underlying_type == null || underlying_type.IsPublic || underlying_type.IsNestedPublic;
585                 }
586
587                 protected virtual bool LookupIsUnknown ()
588                 {
589                         return UnderlyingType == null;
590                 }
591
592                 protected virtual bool LookupIsWhitespaceSignificantCollection ()
593                 {
594                         // probably for unknown types, it should preserve whitespaces.
595                         return IsUnknown || this.GetCustomAttribute<WhitespaceSignificantCollectionAttribute> () != null;
596                 }
597
598                 protected virtual bool LookupIsXData ()
599                 {
600                         // huh? XamlLanguage.XData.IsXData returns false(!)
601                         // return typeof (XData).IsAssignableFrom (UnderlyingType);
602                         return false;
603                 }
604
605                 protected virtual XamlType LookupItemType ()
606                 {
607                         if (IsArray)
608                                 return new XamlType (type.GetElementType (), SchemaContext);
609                         if (IsDictionary) {
610                                 if (!IsGeneric)
611                                         return new XamlType (typeof (object), SchemaContext);
612                                 return new XamlType (type.GetGenericArguments () [1], SchemaContext);
613                         }
614                         if (!IsCollection)
615                                 return null;
616                         if (!IsGeneric)
617                                 return new XamlType (typeof (object), SchemaContext);
618                         return new XamlType (type.GetGenericArguments () [0], SchemaContext);
619                 }
620
621                 protected virtual XamlType LookupKeyType ()
622                 {
623                         if (!IsDictionary)
624                                 return null;
625                         if (!IsGeneric)
626                                 return new XamlType (typeof (object), SchemaContext);
627                         return new XamlType (type.GetGenericArguments () [0], SchemaContext);
628                 }
629
630                 protected virtual XamlType LookupMarkupExtensionReturnType ()
631                 {
632                         var a = this.GetCustomAttribute<MarkupExtensionReturnTypeAttribute> ();
633                         return a != null ? new XamlType (a.ReturnType, SchemaContext) : null;
634                 }
635
636                 protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
637                 {
638                         // FIXME: verify if this does not filter out events.
639                         return GetAllMembers ().FirstOrDefault (m => m.Name == name && (skipReadOnlyCheck || !m.IsReadOnly || m.Type.IsCollection || m.Type.IsDictionary || m.Type.IsArray));
640                 }
641
642                 protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
643                 {
644                         if (UnderlyingType == null/* || !IsMarkupExtension*/) // see nunit tests...
645                                 return null;
646
647                         // check if there is applicable ConstructorArgumentAttribute.
648                         // If there is, then return its type.
649                         if (parameterCount == 1) {
650                                 foreach (var xm in GetAllMembers ()) {
651                                         var ca = xm.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false);
652                                         if (ca != null)
653                                                 return new XamlType [] {xm.Type};
654                                 }
655                         }
656
657                         var methods = (from m in UnderlyingType.GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) where m.GetParameters ().Length == parameterCount select m).ToArray ();
658                         if (methods.Length == 1)
659                                 return (from p in methods [0].GetParameters () select SchemaContext.GetXamlType (p.ParameterType)).ToArray ();
660
661                         if (SchemaContext.SupportMarkupExtensionsWithDuplicateArity)
662                                 throw new NotSupportedException ("The default LookupPositionalParameters implementation does not allow duplicate arity of markup extensions");
663                         return null;
664                 }
665
666                 BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
667
668                 protected virtual EventHandler<XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler ()
669                 {
670                         var a = this.GetCustomAttribute<XamlSetMarkupExtensionAttribute> ();
671                         if (a == null)
672                                 return null;
673                         var mi = type.GetMethod (a.XamlSetMarkupExtensionHandler, flags_get_static);
674                         if (mi == null)
675                                 throw new ArgumentException ("Binding to XamlSetMarkupExtensionHandler failed");
676                         return (EventHandler<XamlSetMarkupExtensionEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetMarkupExtensionEventArgs>), mi);
677                 }
678
679                 protected virtual EventHandler<XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler ()
680                 {
681                         var a = this.GetCustomAttribute<XamlSetTypeConverterAttribute> ();
682                         if (a == null)
683                                 return null;
684                         var mi = type.GetMethod (a.XamlSetTypeConverterHandler, flags_get_static);
685                         if (mi == null)
686                                 throw new ArgumentException ("Binding to XamlSetTypeConverterHandler failed");
687                         return (EventHandler<XamlSetTypeConverterEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetTypeConverterEventArgs>), mi);
688                 }
689
690                 protected virtual bool LookupTrimSurroundingWhitespace ()
691                 {
692                         return this.GetCustomAttribute<TrimSurroundingWhitespaceAttribute> () != null;
693                 }
694
695                 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
696                 {
697                         var t = UnderlyingType;
698                         if (t == null)
699                                 return null;
700
701                         // equivalent to TypeExtension.
702                         // FIXME: not sure if it should be specially handled here.
703                         if (t == typeof (Type))
704                                 t = typeof (TypeExtension);
705
706                         var a = GetCustomAttributeProvider ();
707                         var ca = a != null ? a.GetCustomAttribute<TypeConverterAttribute> (false) : null;
708                         if (ca != null)
709                                 return SchemaContext.GetValueConverter<TypeConverter> (Type.GetType (ca.ConverterTypeName), this);
710
711                         if (t == typeof (object)) // This is a special case. ConverterType is null.
712                                 return SchemaContext.GetValueConverter<TypeConverter> (null, this);
713
714                         // It's still not decent to check CollectionConverter.
715                         var tct = TypeDescriptor.GetConverter (t).GetType ();
716                         if (tct != typeof (TypeConverter) && tct != typeof (CollectionConverter) && tct != typeof (ReferenceConverter))
717                                 return SchemaContext.GetValueConverter<TypeConverter> (tct, this);
718                         return null;
719                 }
720
721                 protected virtual Type LookupUnderlyingType ()
722                 {
723                         return underlying_type;
724                 }
725
726                 protected virtual bool LookupUsableDuringInitialization ()
727                 {
728                         var a = this.GetCustomAttribute<UsableDuringInitializationAttribute> ();
729                         return a != null && a.Usable;
730                 }
731
732                 static XamlValueConverter<ValueSerializer> string_value_serializer;
733
734                 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
735                 {
736                         return LookupValueSerializer (this, GetCustomAttributeProvider ());
737                 }
738
739                 internal static XamlValueConverter<ValueSerializer> LookupValueSerializer (XamlType targetType, ICustomAttributeProvider provider)
740                 {
741                         if (provider == null)
742                                 return null;
743
744                         var a = provider.GetCustomAttribute<ValueSerializerAttribute> (true);
745                         if (a != null)
746                                 return new XamlValueConverter<ValueSerializer> (a.ValueSerializerType ?? Type.GetType (a.ValueSerializerTypeName), targetType);
747
748                         if (targetType.BaseType != null) {
749                                 var ret = targetType.BaseType.LookupValueSerializer ();
750                                 if (ret != null)
751                                         return ret;
752                         }
753
754                         if (targetType.UnderlyingType == typeof (string)) {
755                                 if (string_value_serializer == null)
756                                         string_value_serializer = new XamlValueConverter<ValueSerializer> (typeof (StringValueSerializer), targetType);
757                                 return string_value_serializer;
758                         }
759
760                         return null;
761                 }
762
763                 static string GetXamlName (Type type)
764                 {
765                         string n;
766                         if (!type.IsNested)
767                                 n = type.Name;
768                         else
769                                 n = GetXamlName (type.DeclaringType) + "+" + type.Name;
770                         if (type.IsGenericType && !type.ContainsGenericParameters) // the latter condition is to filter out "nested non-generic type within generic type".
771                                 return n.Substring (0, n.IndexOf ('`'));
772                         else
773                                 return n;
774                 }
775         }
776 }