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