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