2010-04-09 Atsushi Enomoto <atsushi@ximian.com>
[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                 }
80
81                 protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
82                         : this (String.Empty, typeName, typeArguments, schemaContext)
83                 {
84                 }
85
86                 XamlType (XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
87                 {
88                         if (schemaContext == null)
89                                 throw new ArgumentNullException ("schemaContext");
90                         SchemaContext = schemaContext;
91                         this.invoker = invoker ?? new XamlTypeInvoker (this);
92                 }
93
94                 Type type, underlying_type;
95
96                 // populated properties
97                 XamlType base_type;
98                 XamlTypeInvoker invoker;
99
100                 internal EventHandler<XamlSetMarkupExtensionEventArgs> SetMarkupExtensionHandler {
101                         get { return LookupSetMarkupExtensionHandler (); }
102                 }
103
104                 internal EventHandler<XamlSetTypeConverterEventArgs> SetTypeConverterHandler {
105                         get { return LookupSetTypeConverterHandler (); }
106                 }
107
108                 public IList<XamlType> AllowedContentTypes {
109                         get { return LookupAllowedContentTypes (); }
110                 }
111
112                 public XamlType BaseType {
113                         get { return LookupBaseType (); }
114                 }
115
116                 public bool ConstructionRequiresArguments {
117                         get { return LookupConstructionRequiresArguments (); }
118                 }
119
120                 public XamlMember ContentProperty {
121                         get { return LookupContentProperty (); }
122                 }
123
124                 public IList<XamlType> ContentWrappers {
125                         get { return LookupContentWrappers (); }
126                 }
127
128                 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
129                         get { return LookupDeferringLoader (); }
130                 }
131
132                 public XamlTypeInvoker Invoker {
133                         get { return LookupInvoker (); }
134                 }
135
136                 public bool IsAmbient {
137                         get { return LookupIsAmbient (); }
138                 }
139                 public bool IsArray {
140                         get { return type.IsArray; }
141                 }
142                 public bool IsCollection {
143                         // it somehow treats array as not a collection...
144                         get { return !type.IsArray && type.ImplementsAnyInterfacesOf (typeof (ICollection), typeof (ICollection<>)); }
145                 }
146
147                 public bool IsConstructible {
148                         get { return LookupIsConstructible (); }
149                 }
150
151                 public bool IsDictionary {
152                         get { return type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)); }
153                 }
154
155                 public bool IsGeneric {
156                         get { return type.IsGenericType; }
157                 }
158
159                 public bool IsMarkupExtension {
160                         get { return LookupIsMarkupExtension (); }
161                 }
162                 public bool IsNameScope {
163                         get { return LookupIsNameScope (); }
164                 }
165                 public bool IsNameValid {
166                         get { return XamlLanguage.IsValidXamlName (Name); }
167                 }
168
169                 public bool IsNullable {
170                         get { return LookupIsNullable (); }
171                 }
172
173                 public bool IsPublic {
174                         get { return LookupIsPublic (); }
175                 }
176
177                 public bool IsUnknown {
178                         get { return LookupIsUnknown (); }
179                 }
180
181                 public bool IsUsableDuringInitialization {
182                         get { return LookupUsableDuringInitialization (); }
183                 }
184
185                 public bool IsWhitespaceSignificantCollection {
186                         get { return LookupIsWhitespaceSignificantCollection (); }
187                 }
188
189                 public bool IsXData {
190                         get { return LookupIsXData (); }
191                 }
192
193                 public XamlType ItemType {
194                         get { return LookupItemType (); }
195                 }
196
197                 public XamlType KeyType {
198                         get { return LookupKeyType (); }
199                 }
200
201                 public XamlType MarkupExtensionReturnType {
202                         get { return LookupMarkupExtensionReturnType (); }
203                 }
204
205                 public string Name { get; private set; }
206
207                 public string PreferredXamlNamespace { get; private set; }
208
209                 public XamlSchemaContext SchemaContext { get; private set; }
210
211                 public bool TrimSurroundingWhitespace {
212                         get { return LookupTrimSurroundingWhitespace (); }
213                 }
214
215                 public IList<XamlType> TypeArguments { get; private set; }
216
217                 public XamlValueConverter<TypeConverter> TypeConverter {
218                         get { return LookupTypeConverter (); }
219                 }
220
221                 public Type UnderlyingType {
222                         get { return LookupUnderlyingType (); }
223                 }
224
225                 public XamlValueConverter<ValueSerializer> ValueSerializer {
226                         get { return LookupValueSerializer (); }
227                 }
228
229                 public static bool operator == (XamlType left, XamlType right)
230                 {
231                         return IsNull (left) ? IsNull (right) : left.Equals (right);
232                 }
233
234                 static bool IsNull (XamlType a)
235                 {
236                         return Object.ReferenceEquals (a, null);
237                 }
238
239                 public static bool operator != (XamlType left, XamlType right)
240                 {
241                         return !(left == right);
242                 }
243                 
244                 public bool Equals (XamlType other)
245                 {
246                         return !IsNull (other) &&
247                                 UnderlyingType == other.UnderlyingType &&
248                                 Name == other.Name &&
249                                 PreferredXamlNamespace == other.PreferredXamlNamespace &&
250                                 CompareTypes (TypeArguments, other.TypeArguments);
251                 }
252
253                 static bool CompareTypes (IList<XamlType> a1, IList<XamlType> a2)
254                 {
255                         if (a1 == null)
256                                 return a2 == null;
257                         if (a2 == null)
258                                 return false;
259                         if (a1.Count != a2.Count)
260                                 return false;
261                         for (int i = 0; i < a1.Count; i++)
262                                 if (a1 [i] != a2 [i])
263                                         return false;
264                         return true;
265                 }
266
267                 public override bool Equals (object obj)
268                 {
269                         var a = obj as XamlType;
270                         return Equals (a);
271                 }
272                 
273                 public override int GetHashCode ()
274                 {
275                         if (UnderlyingType != null)
276                                 return UnderlyingType.GetHashCode ();
277                         int x = Name.GetHashCode () << 7 + PreferredXamlNamespace.GetHashCode ();
278                         if (TypeArguments != null)
279                                 foreach (var t in TypeArguments)
280                                         x = t.GetHashCode () + x << 5;
281                         return x;
282                 }
283
284                 public override string ToString ()
285                 {
286                         return UnderlyingType != null ? UnderlyingType.ToString () : String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
287                 }
288
289                 public virtual bool CanAssignTo (XamlType xamlType)
290                 {
291                         throw new NotImplementedException ();
292                 }
293
294                 public XamlMember GetAliasedProperty (XamlDirective directive)
295                 {
296                         return LookupAliasedProperty (directive);
297                 }
298
299                 public ICollection<XamlMember> GetAllAttachableMembers ()
300                 {
301                         return new List<XamlMember> (LookupAllAttachableMembers ());
302                 }
303
304                 public ICollection<XamlMember> GetAllMembers ()
305                 {
306                         return new List<XamlMember> (LookupAllMembers ());
307                 }
308
309                 public XamlMember GetAttachableMember (string name)
310                 {
311                         return LookupAttachableMember (name);
312                 }
313
314                 public XamlMember GetMember (string name)
315                 {
316                         return LookupMember (name, false);
317                 }
318
319                 public IList<XamlType> GetPositionalParameters (int parameterCount)
320                 {
321                         return LookupPositionalParameters (parameterCount);
322                 }
323
324                 public virtual IList<string> GetXamlNamespaces ()
325                 {
326                         throw new NotImplementedException ();
327                 }
328
329                 // lookups
330
331                 protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
332                 {
333                         throw new NotImplementedException ();
334                 }
335                 protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
336                 {
337                         throw new NotImplementedException ();
338                 }
339                 protected virtual IEnumerable<XamlMember> LookupAllMembers ()
340                 {
341                         throw new NotImplementedException ();
342                 }
343                 protected virtual IList<XamlType> LookupAllowedContentTypes ()
344                 {
345                         throw new NotImplementedException ();
346                 }
347                 protected virtual XamlMember LookupAttachableMember (string name)
348                 {
349                         throw new NotImplementedException ();
350                 }
351
352                 [MonoTODO]
353                 protected virtual XamlType LookupBaseType ()
354                 {
355                         if (base_type == null) {
356                                 if (UnderlyingType == null)
357                                         // FIXME: probably something advanced is needed here.
358                                         base_type = new XamlType (typeof (object), SchemaContext, Invoker);
359                                 else
360                                         base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
361                         }
362                         return base_type;
363                 }
364
365                 protected virtual XamlCollectionKind LookupCollectionKind ()
366                 {
367                         throw new NotImplementedException ();
368                 }
369
370                 protected virtual bool LookupConstructionRequiresArguments ()
371                 {
372                         if (UnderlyingType == null)
373                                 return false;
374
375                         // not sure if it is required, but TypeDefinition and MemberDefinition return true while they are abstract and it makes no sense.
376                         if (UnderlyingType.IsAbstract)
377                                 return true;
378
379                         // FIXME: probably some primitive types are treated as special.
380                         switch (Type.GetTypeCode (UnderlyingType)) {
381                         case TypeCode.String:
382                                 return true;
383                         case TypeCode.Object:
384                                 if (UnderlyingType == typeof (TimeSpan))
385                                         return false;
386                                 break;
387                         default:
388                                 return false;
389                         }
390
391                         return UnderlyingType.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) == null;
392                 }
393
394                 protected virtual XamlMember LookupContentProperty ()
395                 {
396                         var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
397                         return a != null && a.Name != null ? GetMember (a.Name) : null;
398                 }
399                 protected virtual IList<XamlType> LookupContentWrappers ()
400                 {
401                         throw new NotImplementedException ();
402                 }
403                 protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
404                 {
405                         throw new NotImplementedException ();
406                 }
407                 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
408                 {
409                         throw new NotImplementedException ();
410                 }
411
412                 protected virtual XamlTypeInvoker LookupInvoker ()
413                 {
414                         return invoker;
415                 }
416
417                 protected virtual bool LookupIsAmbient ()
418                 {
419                         return this.GetCustomAttribute<AmbientAttribute> () != null;
420                 }
421
422                 protected virtual bool LookupIsConstructible ()
423                 {
424                         // see spec. 5.2.
425                         if (IsArray) // x:Array
426                                 return false;
427                         if (type == typeof (XamlType)) // x:XamlType
428                                 return false;
429                         // FIXME: handle x:XamlEvent
430                         if (IsMarkupExtension)
431                                 return false;
432                         // FIXME: handle x:Code
433                         // FIXME: commented out.
434                         //if (IsXData)
435                         //      return false;
436
437                         // FIXME: this check is extraneous to spec. 5.2.
438                         if (ConstructionRequiresArguments)
439                                 return false;
440
441                         return true;
442                 }
443
444                 protected virtual bool LookupIsMarkupExtension ()
445                 {
446                         return typeof (MarkupExtension).IsAssignableFrom (UnderlyingType);
447                 }
448
449                 protected virtual bool LookupIsNameScope ()
450                 {
451                         return typeof (INameScope).IsAssignableFrom (UnderlyingType);
452                 }
453
454                 protected virtual bool LookupIsNullable ()
455                 {
456                         return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
457                 }
458
459                 protected virtual bool LookupIsPublic ()
460                 {
461                         return underlying_type == null || underlying_type.IsPublic || underlying_type.IsNestedPublic;
462                 }
463
464                 protected virtual bool LookupIsUnknown ()
465                 {
466                         return UnderlyingType == null;
467                 }
468
469                 protected virtual bool LookupIsWhitespaceSignificantCollection ()
470                 {
471                         // probably for unknown types, it should preserve whitespaces.
472                         return IsUnknown || this.GetCustomAttribute<WhitespaceSignificantCollectionAttribute> () != null;
473                 }
474
475                 protected virtual bool LookupIsXData ()
476                 {
477                         // huh? XamlLanguage.XData.IsXData returns false(!)
478                         // return typeof (XData).IsAssignableFrom (UnderlyingType);
479                         return false;
480                 }
481
482                 protected virtual XamlType LookupItemType ()
483                 {
484                         if (IsArray)
485                                 return new XamlType (type.GetElementType (), SchemaContext);
486                         if (!IsCollection)
487                                 return null;
488                         if (!IsGeneric)
489                                 return new XamlType (typeof (object), SchemaContext);
490                         return new XamlType (type.GetGenericArguments () [0], SchemaContext);
491                 }
492
493                 protected virtual XamlType LookupKeyType ()
494                 {
495                         if (!IsDictionary)
496                                 return null;
497                         if (!IsGeneric)
498                                 return new XamlType (typeof (object), SchemaContext);
499                         return new XamlType (type.GetGenericArguments () [0], SchemaContext);
500                 }
501
502                 protected virtual XamlType LookupMarkupExtensionReturnType ()
503                 {
504                         var a = this.GetCustomAttribute<MarkupExtensionReturnTypeAttribute> ();
505                         return a != null ? new XamlType (a.ReturnType, SchemaContext) : null;
506                 }
507
508                 protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
509                 {
510                         var pi = UnderlyingType.GetProperty (name);
511                         if (pi != null && (skipReadOnlyCheck || pi.CanWrite))
512                                 return new XamlMember (pi, SchemaContext);
513                         var ei = UnderlyingType.GetEvent (name);
514                         if (ei != null)
515                                 return new XamlMember (ei, SchemaContext);
516                         return null;
517                 }
518
519                 protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
520                 {
521                         throw new NotImplementedException ();
522                 }
523
524                 BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
525
526                 protected virtual EventHandler<XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler ()
527                 {
528                         var a = this.GetCustomAttribute<XamlSetMarkupExtensionAttribute> ();
529                         if (a == null)
530                                 return null;
531                         var mi = type.GetMethod (a.XamlSetMarkupExtensionHandler, flags_get_static);
532                         if (mi == null)
533                                 throw new ArgumentException ("Binding to XamlSetMarkupExtensionHandler failed");
534                         return (EventHandler<XamlSetMarkupExtensionEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetMarkupExtensionEventArgs>), mi);
535                 }
536
537                 protected virtual EventHandler<XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler ()
538                 {
539                         var a = this.GetCustomAttribute<XamlSetTypeConverterAttribute> ();
540                         if (a == null)
541                                 return null;
542                         var mi = type.GetMethod (a.XamlSetTypeConverterHandler, flags_get_static);
543                         if (mi == null)
544                                 throw new ArgumentException ("Binding to XamlSetTypeConverterHandler failed");
545                         return (EventHandler<XamlSetTypeConverterEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetTypeConverterEventArgs>), mi);
546                 }
547
548                 protected virtual bool LookupTrimSurroundingWhitespace ()
549                 {
550                         return this.GetCustomAttribute<TrimSurroundingWhitespaceAttribute> () != null;
551                 }
552
553                 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
554                 {
555                         throw new NotImplementedException ();
556                 }
557
558                 protected virtual Type LookupUnderlyingType ()
559                 {
560                         return underlying_type;
561                 }
562
563                 protected virtual bool LookupUsableDuringInitialization ()
564                 {
565                         var a = this.GetCustomAttribute<UsableDuringInitializationAttribute> ();
566                         return a != null && a.Usable;
567                 }
568
569                 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
570                 {
571                         throw new NotImplementedException ();
572                 }
573         }
574 }