2 // Copyright (C) 2010 Novell Inc. http://novell.com
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
24 using System.Collections;
25 using System.Collections.Generic;
26 using System.ComponentModel;
28 using System.Reflection;
29 using System.Windows.Markup;
30 using System.Xaml.Schema;
34 public class XamlType : IEquatable<XamlType>
36 public XamlType (Type underlyingType, XamlSchemaContext schemaContext)
37 : this (underlyingType, schemaContext, null)
41 static readonly Type [] predefined_types = {
42 typeof (XData), typeof (Uri), typeof (TimeSpan), typeof (PropertyDefinition), typeof (MemberDefinition), typeof (Reference)
45 public XamlType (Type underlyingType, XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
46 : this (schemaContext, invoker)
48 if (underlyingType == null)
49 throw new ArgumentNullException ("underlyingType");
50 type = underlyingType;
51 underlying_type = type;
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;
59 PreferredXamlNamespace = String.Format ("clr-namespace:{0};assembly={1}", type.Namespace, type.Assembly.GetName ().Name);
62 PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
65 public XamlType (string unknownTypeNamespace, string unknownTypeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
66 : this (schemaContext, null)
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");
75 type = typeof (object);
76 Name = unknownTypeName;
77 PreferredXamlNamespace = unknownTypeNamespace;
78 TypeArguments = typeArguments != null && typeArguments.Count == 0 ? null : typeArguments;
81 protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
82 : this (String.Empty, typeName, typeArguments, schemaContext)
86 XamlType (XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
88 if (schemaContext == null)
89 throw new ArgumentNullException ("schemaContext");
90 SchemaContext = schemaContext;
91 this.invoker = invoker ?? new XamlTypeInvoker (this);
94 Type type, underlying_type;
96 // populated properties
98 XamlTypeInvoker invoker;
100 internal EventHandler<XamlSetMarkupExtensionEventArgs> SetMarkupExtensionHandler {
101 get { return LookupSetMarkupExtensionHandler (); }
104 internal EventHandler<XamlSetTypeConverterEventArgs> SetTypeConverterHandler {
105 get { return LookupSetTypeConverterHandler (); }
108 public IList<XamlType> AllowedContentTypes {
109 get { return LookupAllowedContentTypes (); }
112 public XamlType BaseType {
113 get { return LookupBaseType (); }
116 public bool ConstructionRequiresArguments {
117 get { return LookupConstructionRequiresArguments (); }
120 public XamlMember ContentProperty {
121 get { return LookupContentProperty (); }
124 public IList<XamlType> ContentWrappers {
125 get { return LookupContentWrappers (); }
128 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
129 get { return LookupDeferringLoader (); }
132 public XamlTypeInvoker Invoker {
133 get { return LookupInvoker (); }
136 public bool IsAmbient {
137 get { return LookupIsAmbient (); }
139 public bool IsArray {
140 get { return type.IsArray; }
142 public bool IsCollection {
143 // it somehow treats array as not a collection...
144 get { return !type.IsArray && type.ImplementsAnyInterfacesOf (typeof (ICollection), typeof (ICollection<>)); }
147 public bool IsConstructible {
148 get { return LookupIsConstructible (); }
151 public bool IsDictionary {
152 get { return type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)); }
155 public bool IsGeneric {
156 get { return type.IsGenericType; }
159 public bool IsMarkupExtension {
160 get { return LookupIsMarkupExtension (); }
162 public bool IsNameScope {
163 get { return LookupIsNameScope (); }
165 public bool IsNameValid {
166 get { return XamlLanguage.IsValidXamlName (Name); }
169 public bool IsNullable {
170 get { return LookupIsNullable (); }
173 public bool IsPublic {
174 get { return LookupIsPublic (); }
177 public bool IsUnknown {
178 get { return LookupIsUnknown (); }
181 public bool IsUsableDuringInitialization {
182 get { return LookupUsableDuringInitialization (); }
185 public bool IsWhitespaceSignificantCollection {
186 get { return LookupIsWhitespaceSignificantCollection (); }
189 public bool IsXData {
190 get { return LookupIsXData (); }
193 public XamlType ItemType {
194 get { return LookupItemType (); }
197 public XamlType KeyType {
198 get { return LookupKeyType (); }
201 public XamlType MarkupExtensionReturnType {
202 get { return LookupMarkupExtensionReturnType (); }
205 public string Name { get; private set; }
207 public string PreferredXamlNamespace { get; private set; }
209 public XamlSchemaContext SchemaContext { get; private set; }
211 public bool TrimSurroundingWhitespace {
212 get { return LookupTrimSurroundingWhitespace (); }
215 public IList<XamlType> TypeArguments { get; private set; }
217 public XamlValueConverter<TypeConverter> TypeConverter {
218 get { return LookupTypeConverter (); }
221 public Type UnderlyingType {
222 get { return LookupUnderlyingType (); }
225 public XamlValueConverter<ValueSerializer> ValueSerializer {
226 get { return LookupValueSerializer (); }
229 public static bool operator == (XamlType left, XamlType right)
231 return IsNull (left) ? IsNull (right) : left.Equals (right);
234 static bool IsNull (XamlType a)
236 return Object.ReferenceEquals (a, null);
239 public static bool operator != (XamlType left, XamlType right)
241 return !(left == right);
244 public bool Equals (XamlType other)
246 return !IsNull (other) &&
247 UnderlyingType == other.UnderlyingType &&
248 Name == other.Name &&
249 PreferredXamlNamespace == other.PreferredXamlNamespace &&
250 CompareTypes (TypeArguments, other.TypeArguments);
253 static bool CompareTypes (IList<XamlType> a1, IList<XamlType> a2)
259 if (a1.Count != a2.Count)
261 for (int i = 0; i < a1.Count; i++)
262 if (a1 [i] != a2 [i])
267 public override bool Equals (object obj)
269 var a = obj as XamlType;
273 public override int GetHashCode ()
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;
284 public override string ToString ()
286 return UnderlyingType != null ? UnderlyingType.ToString () : String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
289 public virtual bool CanAssignTo (XamlType xamlType)
291 throw new NotImplementedException ();
294 public XamlMember GetAliasedProperty (XamlDirective directive)
296 return LookupAliasedProperty (directive);
299 public ICollection<XamlMember> GetAllAttachableMembers ()
301 return new List<XamlMember> (LookupAllAttachableMembers ());
304 public ICollection<XamlMember> GetAllMembers ()
306 return new List<XamlMember> (LookupAllMembers ());
309 public XamlMember GetAttachableMember (string name)
311 return LookupAttachableMember (name);
314 public XamlMember GetMember (string name)
316 return LookupMember (name, false);
319 public IList<XamlType> GetPositionalParameters (int parameterCount)
321 return LookupPositionalParameters (parameterCount);
324 public virtual IList<string> GetXamlNamespaces ()
326 throw new NotImplementedException ();
331 protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
333 throw new NotImplementedException ();
335 protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
337 throw new NotImplementedException ();
339 protected virtual IEnumerable<XamlMember> LookupAllMembers ()
341 throw new NotImplementedException ();
343 protected virtual IList<XamlType> LookupAllowedContentTypes ()
345 throw new NotImplementedException ();
347 protected virtual XamlMember LookupAttachableMember (string name)
349 throw new NotImplementedException ();
353 protected virtual XamlType LookupBaseType ()
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);
360 base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
365 protected virtual XamlCollectionKind LookupCollectionKind ()
367 throw new NotImplementedException ();
370 protected virtual bool LookupConstructionRequiresArguments ()
372 if (UnderlyingType == null)
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)
379 // FIXME: probably some primitive types are treated as special.
380 switch (Type.GetTypeCode (UnderlyingType)) {
381 case TypeCode.String:
383 case TypeCode.Object:
384 if (UnderlyingType == typeof (TimeSpan))
391 return UnderlyingType.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) == null;
394 protected virtual XamlMember LookupContentProperty ()
396 var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
397 return a != null && a.Name != null ? GetMember (a.Name) : null;
399 protected virtual IList<XamlType> LookupContentWrappers ()
401 throw new NotImplementedException ();
403 protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
405 throw new NotImplementedException ();
407 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
409 throw new NotImplementedException ();
412 protected virtual XamlTypeInvoker LookupInvoker ()
417 protected virtual bool LookupIsAmbient ()
419 return this.GetCustomAttribute<AmbientAttribute> () != null;
422 protected virtual bool LookupIsConstructible ()
425 if (IsArray) // x:Array
427 if (type == typeof (XamlType)) // x:XamlType
429 // FIXME: handle x:XamlEvent
430 if (IsMarkupExtension)
432 // FIXME: handle x:Code
433 // FIXME: commented out.
437 // FIXME: this check is extraneous to spec. 5.2.
438 if (ConstructionRequiresArguments)
444 protected virtual bool LookupIsMarkupExtension ()
446 return typeof (MarkupExtension).IsAssignableFrom (UnderlyingType);
449 protected virtual bool LookupIsNameScope ()
451 return typeof (INameScope).IsAssignableFrom (UnderlyingType);
454 protected virtual bool LookupIsNullable ()
456 return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
459 protected virtual bool LookupIsPublic ()
461 return underlying_type == null || underlying_type.IsPublic || underlying_type.IsNestedPublic;
464 protected virtual bool LookupIsUnknown ()
466 return UnderlyingType == null;
469 protected virtual bool LookupIsWhitespaceSignificantCollection ()
471 // probably for unknown types, it should preserve whitespaces.
472 return IsUnknown || this.GetCustomAttribute<WhitespaceSignificantCollectionAttribute> () != null;
475 protected virtual bool LookupIsXData ()
477 // huh? XamlLanguage.XData.IsXData returns false(!)
478 // return typeof (XData).IsAssignableFrom (UnderlyingType);
482 protected virtual XamlType LookupItemType ()
485 return new XamlType (type.GetElementType (), SchemaContext);
489 return new XamlType (typeof (object), SchemaContext);
490 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
493 protected virtual XamlType LookupKeyType ()
498 return new XamlType (typeof (object), SchemaContext);
499 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
502 protected virtual XamlType LookupMarkupExtensionReturnType ()
504 var a = this.GetCustomAttribute<MarkupExtensionReturnTypeAttribute> ();
505 return a != null ? new XamlType (a.ReturnType, SchemaContext) : null;
508 protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
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);
515 return new XamlMember (ei, SchemaContext);
519 protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
521 throw new NotImplementedException ();
524 BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
526 protected virtual EventHandler<XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler ()
528 var a = this.GetCustomAttribute<XamlSetMarkupExtensionAttribute> ();
531 var mi = type.GetMethod (a.XamlSetMarkupExtensionHandler, flags_get_static);
533 throw new ArgumentException ("Binding to XamlSetMarkupExtensionHandler failed");
534 return (EventHandler<XamlSetMarkupExtensionEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetMarkupExtensionEventArgs>), mi);
537 protected virtual EventHandler<XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler ()
539 var a = this.GetCustomAttribute<XamlSetTypeConverterAttribute> ();
542 var mi = type.GetMethod (a.XamlSetTypeConverterHandler, flags_get_static);
544 throw new ArgumentException ("Binding to XamlSetTypeConverterHandler failed");
545 return (EventHandler<XamlSetTypeConverterEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetTypeConverterEventArgs>), mi);
548 protected virtual bool LookupTrimSurroundingWhitespace ()
550 return this.GetCustomAttribute<TrimSurroundingWhitespaceAttribute> () != null;
553 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
555 var t = UnderlyingType;
559 if (System.Type.GetTypeCode (t) != TypeCode.Object)
560 return new XamlValueConverter<TypeConverter> (TypeDescriptor.GetConverter (t).GetType (), this);
562 if (t == typeof (TypeExtension))
563 return new XamlValueConverter<TypeConverter> (typeof (TypeExtensionConverter), this);
564 if (t == typeof (StaticExtension))
565 return new XamlValueConverter<TypeConverter> (typeof (StaticExtensionConverter), this);
566 if (t == typeof (TimeSpan))
567 return new XamlValueConverter<TypeConverter> (typeof (TimeSpanConverter), this);
568 if (t == typeof (Uri))
569 return new XamlValueConverter<TypeConverter> (typeof (UriTypeConverter), this);
570 if (t == typeof (object))
571 return new XamlValueConverter<TypeConverter> (typeof (TypeConverter), this);
576 protected virtual Type LookupUnderlyingType ()
578 return underlying_type;
581 protected virtual bool LookupUsableDuringInitialization ()
583 var a = this.GetCustomAttribute<UsableDuringInitializationAttribute> ();
584 return a != null && a.Usable;
587 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
589 throw new NotImplementedException ();