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