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