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;
79 explicit_ns = unknownTypeNamespace;
82 protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
83 : this (String.Empty, typeName, typeArguments, schemaContext)
87 XamlType (XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
89 if (schemaContext == null)
90 throw new ArgumentNullException ("schemaContext");
91 SchemaContext = schemaContext;
92 this.invoker = invoker ?? new XamlTypeInvoker (this);
95 Type type, underlying_type;
99 // populated properties
101 XamlTypeInvoker invoker;
103 internal EventHandler<XamlSetMarkupExtensionEventArgs> SetMarkupExtensionHandler {
104 get { return LookupSetMarkupExtensionHandler (); }
107 internal EventHandler<XamlSetTypeConverterEventArgs> SetTypeConverterHandler {
108 get { return LookupSetTypeConverterHandler (); }
111 public IList<XamlType> AllowedContentTypes {
112 get { return LookupAllowedContentTypes (); }
115 public XamlType BaseType {
116 get { return LookupBaseType (); }
119 public bool ConstructionRequiresArguments {
120 get { return LookupConstructionRequiresArguments (); }
123 public XamlMember ContentProperty {
124 get { return LookupContentProperty (); }
127 public IList<XamlType> ContentWrappers {
128 get { return LookupContentWrappers (); }
131 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
132 get { return LookupDeferringLoader (); }
135 public XamlTypeInvoker Invoker {
136 get { return LookupInvoker (); }
139 public bool IsAmbient {
140 get { return LookupIsAmbient (); }
142 public bool IsArray {
143 get { return type.IsArray; }
145 public bool IsCollection {
146 // it somehow treats array as not a collection...
147 get { return !type.IsArray && type.ImplementsAnyInterfacesOf (typeof (ICollection), typeof (ICollection<>)); }
150 public bool IsConstructible {
151 get { return LookupIsConstructible (); }
154 public bool IsDictionary {
155 get { return type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)); }
158 public bool IsGeneric {
159 get { return type.IsGenericType; }
162 public bool IsMarkupExtension {
163 get { return LookupIsMarkupExtension (); }
165 public bool IsNameScope {
166 get { return LookupIsNameScope (); }
168 public bool IsNameValid {
169 get { return XamlLanguage.IsValidXamlName (Name); }
172 public bool IsNullable {
173 get { return LookupIsNullable (); }
176 public bool IsPublic {
177 get { return LookupIsPublic (); }
180 public bool IsUnknown {
181 get { return LookupIsUnknown (); }
184 public bool IsUsableDuringInitialization {
185 get { return LookupUsableDuringInitialization (); }
188 public bool IsWhitespaceSignificantCollection {
189 get { return LookupIsWhitespaceSignificantCollection (); }
192 public bool IsXData {
193 get { return LookupIsXData (); }
196 public XamlType ItemType {
197 get { return LookupItemType (); }
200 public XamlType KeyType {
201 get { return LookupKeyType (); }
204 public XamlType MarkupExtensionReturnType {
205 get { return LookupMarkupExtensionReturnType (); }
208 public string Name { get; private set; }
210 public string PreferredXamlNamespace { get; private set; }
212 public XamlSchemaContext SchemaContext { get; private set; }
214 public bool TrimSurroundingWhitespace {
215 get { return LookupTrimSurroundingWhitespace (); }
218 public IList<XamlType> TypeArguments { get; private set; }
220 public XamlValueConverter<TypeConverter> TypeConverter {
221 get { return LookupTypeConverter (); }
224 public Type UnderlyingType {
225 get { return LookupUnderlyingType (); }
228 public XamlValueConverter<ValueSerializer> ValueSerializer {
229 get { return LookupValueSerializer (); }
232 public static bool operator == (XamlType left, XamlType right)
234 return IsNull (left) ? IsNull (right) : left.Equals (right);
237 static bool IsNull (XamlType a)
239 return Object.ReferenceEquals (a, null);
242 public static bool operator != (XamlType left, XamlType right)
244 return !(left == right);
247 public bool Equals (XamlType other)
249 return !IsNull (other) &&
250 UnderlyingType == other.UnderlyingType &&
251 Name == other.Name &&
252 PreferredXamlNamespace == other.PreferredXamlNamespace &&
253 CompareTypes (TypeArguments, other.TypeArguments);
256 static bool CompareTypes (IList<XamlType> a1, IList<XamlType> a2)
262 if (a1.Count != a2.Count)
264 for (int i = 0; i < a1.Count; i++)
265 if (a1 [i] != a2 [i])
270 public override bool Equals (object obj)
272 var a = obj as XamlType;
276 public override int GetHashCode ()
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;
287 public override string ToString ()
289 return UnderlyingType != null ? UnderlyingType.ToString () : String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
292 public virtual bool CanAssignTo (XamlType xamlType)
294 throw new NotImplementedException ();
297 public XamlMember GetAliasedProperty (XamlDirective directive)
299 return LookupAliasedProperty (directive);
302 public ICollection<XamlMember> GetAllAttachableMembers ()
304 return new List<XamlMember> (LookupAllAttachableMembers ());
307 public ICollection<XamlMember> GetAllMembers ()
309 return new List<XamlMember> (LookupAllMembers ());
312 public XamlMember GetAttachableMember (string name)
314 return LookupAttachableMember (name);
317 public XamlMember GetMember (string name)
319 return LookupMember (name, false);
322 public IList<XamlType> GetPositionalParameters (int parameterCount)
324 return LookupPositionalParameters (parameterCount);
327 public virtual IList<string> GetXamlNamespaces ()
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 ();
335 return new List<string> (l);
336 return new string [] {String.Empty};
342 protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
344 if (directive == XamlLanguage.Key) {
345 var a = this.GetCustomAttribute<DictionaryKeyPropertyAttribute> ();
346 return a != null ? GetMember (a.Name) : null;
348 if (directive == XamlLanguage.Name) {
349 var a = this.GetCustomAttribute<RuntimeNamePropertyAttribute> ();
350 return a != null ? GetMember (a.Name) : null;
352 if (directive == XamlLanguage.Uid) {
353 var a = this.GetCustomAttribute<UidPropertyAttribute> ();
354 return a != null ? GetMember (a.Name) : null;
356 if (directive == XamlLanguage.Lang) {
357 var a = this.GetCustomAttribute<XmlLangPropertyAttribute> ();
358 return a != null ? GetMember (a.Name) : null;
363 protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
365 if (UnderlyingType == null)
366 return BaseType != null ? BaseType.GetAllMembers () : null;
367 return DoLookupAllAttachableMembers ();
370 IEnumerable<XamlMember> DoLookupAllAttachableMembers ()
372 yield break; // FIXME: what to return here?
375 protected virtual IEnumerable<XamlMember> LookupAllMembers ()
377 if (UnderlyingType == null)
378 return BaseType != null ? BaseType.GetAllMembers () : null;
379 return DoLookupAllMembers ();
382 IEnumerable<XamlMember> DoLookupAllMembers ()
384 foreach (var pi in UnderlyingType.GetProperties ())
385 if (pi.CanRead && pi.CanWrite)
386 yield return new XamlMember (pi, SchemaContext);
389 protected virtual IList<XamlType> LookupAllowedContentTypes ()
391 throw new NotImplementedException ();
393 protected virtual XamlMember LookupAttachableMember (string name)
395 throw new NotImplementedException ();
399 protected virtual XamlType LookupBaseType ()
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);
406 base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
411 // This implementation is not verified. (No place to use.)
412 protected virtual XamlCollectionKind LookupCollectionKind ()
414 if (UnderlyingType == null)
415 return BaseType != null ? BaseType.LookupCollectionKind () : XamlCollectionKind.None;
417 return XamlCollectionKind.Array;
418 // the documented behavior sounds too sloppy though ...
419 var mi = UnderlyingType.GetMethod ("Add");
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;
429 protected virtual bool LookupConstructionRequiresArguments ()
431 if (UnderlyingType == null)
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)
438 // FIXME: probably some primitive types are treated as special.
439 switch (Type.GetTypeCode (UnderlyingType)) {
440 case TypeCode.String:
442 case TypeCode.Object:
443 if (UnderlyingType == typeof (TimeSpan))
450 return UnderlyingType.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) == null;
453 protected virtual XamlMember LookupContentProperty ()
455 var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
456 return a != null && a.Name != null ? GetMember (a.Name) : null;
459 protected virtual IList<XamlType> LookupContentWrappers ()
461 var l = new List<XamlType> ();
462 foreach (ContentWrapperAttribute a in CustomAttributeProvider.GetCustomAttributes (typeof (ContentWrapperAttribute), false))
463 l.Add (SchemaContext.GetXamlType (a.ContentWrapper));
467 internal ICustomAttributeProvider CustomAttributeProvider {
468 get { return LookupCustomAttributeProvider (); }
471 protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
473 return UnderlyingType;
475 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
477 throw new NotImplementedException ();
480 protected virtual XamlTypeInvoker LookupInvoker ()
485 protected virtual bool LookupIsAmbient ()
487 return this.GetCustomAttribute<AmbientAttribute> () != null;
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 ()
495 if (UnderlyingType == null)
497 if (IsMarkupExtension)
499 if (UnderlyingType.IsAbstract)
506 protected virtual bool LookupIsMarkupExtension ()
508 return typeof (MarkupExtension).IsAssignableFrom (UnderlyingType);
511 protected virtual bool LookupIsNameScope ()
513 return typeof (INameScope).IsAssignableFrom (UnderlyingType);
516 protected virtual bool LookupIsNullable ()
518 return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
521 protected virtual bool LookupIsPublic ()
523 return underlying_type == null || underlying_type.IsPublic || underlying_type.IsNestedPublic;
526 protected virtual bool LookupIsUnknown ()
528 return UnderlyingType == null;
531 protected virtual bool LookupIsWhitespaceSignificantCollection ()
533 // probably for unknown types, it should preserve whitespaces.
534 return IsUnknown || this.GetCustomAttribute<WhitespaceSignificantCollectionAttribute> () != null;
537 protected virtual bool LookupIsXData ()
539 // huh? XamlLanguage.XData.IsXData returns false(!)
540 // return typeof (XData).IsAssignableFrom (UnderlyingType);
544 protected virtual XamlType LookupItemType ()
547 return new XamlType (type.GetElementType (), SchemaContext);
551 return new XamlType (typeof (object), SchemaContext);
552 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
555 protected virtual XamlType LookupKeyType ()
560 return new XamlType (typeof (object), SchemaContext);
561 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
564 protected virtual XamlType LookupMarkupExtensionReturnType ()
566 var a = this.GetCustomAttribute<MarkupExtensionReturnTypeAttribute> ();
567 return a != null ? new XamlType (a.ReturnType, SchemaContext) : null;
570 protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
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);
577 return new XamlMember (ei, SchemaContext);
581 protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
583 throw new NotImplementedException ();
586 BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
588 protected virtual EventHandler<XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler ()
590 var a = this.GetCustomAttribute<XamlSetMarkupExtensionAttribute> ();
593 var mi = type.GetMethod (a.XamlSetMarkupExtensionHandler, flags_get_static);
595 throw new ArgumentException ("Binding to XamlSetMarkupExtensionHandler failed");
596 return (EventHandler<XamlSetMarkupExtensionEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetMarkupExtensionEventArgs>), mi);
599 protected virtual EventHandler<XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler ()
601 var a = this.GetCustomAttribute<XamlSetTypeConverterAttribute> ();
604 var mi = type.GetMethod (a.XamlSetTypeConverterHandler, flags_get_static);
606 throw new ArgumentException ("Binding to XamlSetTypeConverterHandler failed");
607 return (EventHandler<XamlSetTypeConverterEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetTypeConverterEventArgs>), mi);
610 protected virtual bool LookupTrimSurroundingWhitespace ()
612 return this.GetCustomAttribute<TrimSurroundingWhitespaceAttribute> () != null;
615 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
617 var t = UnderlyingType;
621 if (t == typeof (object))
622 return SchemaContext.GetValueConverter<TypeConverter> (typeof (TypeConverter), this);
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);
631 protected virtual Type LookupUnderlyingType ()
633 return underlying_type;
636 protected virtual bool LookupUsableDuringInitialization ()
638 var a = this.GetCustomAttribute<UsableDuringInitializationAttribute> ();
639 return a != null && a.Usable;
642 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
644 throw new NotImplementedException ();