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;
54 if (XamlLanguage.InitializingTypes) {
55 Name = type.GetXamlName ();
56 PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
57 } else if ((xt = XamlLanguage.AllTypes.FirstOrDefault (t => t.UnderlyingType == type)) != null) {
59 PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
61 Name = type.GetXamlName ();
62 PreferredXamlNamespace = String.Format ("clr-namespace:{0};assembly={1}", type.Namespace, type.Assembly.GetName ().Name);
66 public XamlType (string unknownTypeNamespace, string unknownTypeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
67 : this (schemaContext, null)
69 if (unknownTypeNamespace == null)
70 throw new ArgumentNullException ("unknownTypeNamespace");
71 if (unknownTypeName == null)
72 throw new ArgumentNullException ("unknownTypeName");
73 if (schemaContext == null)
74 throw new ArgumentNullException ("schemaContext");
76 type = typeof (object);
77 Name = unknownTypeName;
78 PreferredXamlNamespace = unknownTypeNamespace;
79 TypeArguments = typeArguments != null && typeArguments.Count == 0 ? null : typeArguments;
80 explicit_ns = unknownTypeNamespace;
83 protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
84 : this (String.Empty, typeName, typeArguments, schemaContext)
88 XamlType (XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
90 if (schemaContext == null)
91 throw new ArgumentNullException ("schemaContext");
92 SchemaContext = schemaContext;
93 this.invoker = invoker ?? new XamlTypeInvoker (this);
96 Type type, underlying_type;
100 // populated properties
102 XamlTypeInvoker invoker;
104 internal EventHandler<XamlSetMarkupExtensionEventArgs> SetMarkupExtensionHandler {
105 get { return LookupSetMarkupExtensionHandler (); }
108 internal EventHandler<XamlSetTypeConverterEventArgs> SetTypeConverterHandler {
109 get { return LookupSetTypeConverterHandler (); }
112 public IList<XamlType> AllowedContentTypes {
113 get { return LookupAllowedContentTypes (); }
116 public XamlType BaseType {
117 get { return LookupBaseType (); }
120 public bool ConstructionRequiresArguments {
121 get { return LookupConstructionRequiresArguments (); }
124 public XamlMember ContentProperty {
125 get { return LookupContentProperty (); }
128 public IList<XamlType> ContentWrappers {
129 get { return LookupContentWrappers (); }
132 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
133 get { return LookupDeferringLoader (); }
136 public XamlTypeInvoker Invoker {
137 get { return LookupInvoker (); }
140 public bool IsAmbient {
141 get { return LookupIsAmbient (); }
144 public bool IsArray {
145 get { return LookupCollectionKind () == XamlCollectionKind.Array; }
148 // it somehow treats array as not a collection...
149 public bool IsCollection {
150 get { return LookupCollectionKind () == XamlCollectionKind.Collection; }
153 public bool IsConstructible {
154 get { return LookupIsConstructible (); }
157 public bool IsDictionary {
158 get { return LookupCollectionKind () == XamlCollectionKind.Dictionary; }
161 public bool IsGeneric {
162 get { return type.IsGenericType; }
165 public bool IsMarkupExtension {
166 get { return LookupIsMarkupExtension (); }
168 public bool IsNameScope {
169 get { return LookupIsNameScope (); }
171 public bool IsNameValid {
172 get { return XamlLanguage.IsValidXamlName (Name); }
175 public bool IsNullable {
176 get { return LookupIsNullable (); }
179 public bool IsPublic {
180 get { return LookupIsPublic (); }
183 public bool IsUnknown {
184 get { return LookupIsUnknown (); }
187 public bool IsUsableDuringInitialization {
188 get { return LookupUsableDuringInitialization (); }
191 public bool IsWhitespaceSignificantCollection {
192 get { return LookupIsWhitespaceSignificantCollection (); }
195 public bool IsXData {
196 get { return LookupIsXData (); }
199 public XamlType ItemType {
200 get { return LookupItemType (); }
203 public XamlType KeyType {
204 get { return LookupKeyType (); }
207 public XamlType MarkupExtensionReturnType {
208 get { return LookupMarkupExtensionReturnType (); }
211 public string Name { get; private set; }
213 public string PreferredXamlNamespace { get; private set; }
215 public XamlSchemaContext SchemaContext { get; private set; }
217 public bool TrimSurroundingWhitespace {
218 get { return LookupTrimSurroundingWhitespace (); }
221 public IList<XamlType> TypeArguments { get; private set; }
223 public XamlValueConverter<TypeConverter> TypeConverter {
224 get { return LookupTypeConverter (); }
227 public Type UnderlyingType {
228 get { return LookupUnderlyingType (); }
231 public XamlValueConverter<ValueSerializer> ValueSerializer {
232 get { return LookupValueSerializer (); }
235 internal string InternalXmlName {
237 var stn = XamlLanguage.SpecialNames.FirstOrDefault (s => s.Type == this);
238 return stn != null ? stn.Name : Name;
242 public static bool operator == (XamlType left, XamlType right)
244 return IsNull (left) ? IsNull (right) : left.Equals (right);
247 static bool IsNull (XamlType a)
249 return Object.ReferenceEquals (a, null);
252 public static bool operator != (XamlType left, XamlType right)
254 return !(left == right);
257 public bool Equals (XamlType other)
259 return !IsNull (other) &&
260 UnderlyingType == other.UnderlyingType &&
261 Name == other.Name &&
262 PreferredXamlNamespace == other.PreferredXamlNamespace && TypeArguments.ListEquals (other.TypeArguments);
265 public override bool Equals (object obj)
267 var a = obj as XamlType;
271 public override int GetHashCode ()
273 if (UnderlyingType != null)
274 return UnderlyingType.GetHashCode ();
275 int x = Name.GetHashCode () << 7 + PreferredXamlNamespace.GetHashCode ();
276 if (TypeArguments != null)
277 foreach (var t in TypeArguments)
278 x = t.GetHashCode () + x << 5;
282 public override string ToString ()
284 return String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
287 public virtual bool CanAssignTo (XamlType xamlType)
289 throw new NotImplementedException ();
292 public XamlMember GetAliasedProperty (XamlDirective directive)
294 return LookupAliasedProperty (directive);
297 public ICollection<XamlMember> GetAllAttachableMembers ()
299 return new List<XamlMember> (LookupAllAttachableMembers ());
302 public ICollection<XamlMember> GetAllMembers ()
304 return new List<XamlMember> (LookupAllMembers ());
307 public XamlMember GetAttachableMember (string name)
309 return LookupAttachableMember (name);
312 public XamlMember GetMember (string name)
314 return LookupMember (name, false);
317 public IList<XamlType> GetPositionalParameters (int parameterCount)
319 return LookupPositionalParameters (parameterCount);
322 public virtual IList<string> GetXamlNamespaces ()
324 throw new NotImplementedException ();
325 /* this does not work like documented!
326 if (explicit_ns != null)
327 return new string [] {explicit_ns};
328 var l = SchemaContext.GetAllXamlNamespaces ();
330 return new List<string> (l);
331 return new string [] {String.Empty};
337 protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
339 if (directive == XamlLanguage.Key) {
340 var a = this.GetCustomAttribute<DictionaryKeyPropertyAttribute> ();
341 return a != null ? GetMember (a.Name) : null;
343 if (directive == XamlLanguage.Name) {
344 var a = this.GetCustomAttribute<RuntimeNamePropertyAttribute> ();
345 return a != null ? GetMember (a.Name) : null;
347 if (directive == XamlLanguage.Uid) {
348 var a = this.GetCustomAttribute<UidPropertyAttribute> ();
349 return a != null ? GetMember (a.Name) : null;
351 if (directive == XamlLanguage.Lang) {
352 var a = this.GetCustomAttribute<XmlLangPropertyAttribute> ();
353 return a != null ? GetMember (a.Name) : null;
358 protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
360 if (UnderlyingType == null)
361 return BaseType != null ? BaseType.GetAllAttachableMembers () : null;
362 return DoLookupAllAttachableMembers ();
365 IEnumerable<XamlMember> DoLookupAllAttachableMembers ()
367 yield break; // FIXME: what to return here?
370 static readonly XamlMember [] empty_array = new XamlMember [0];
372 protected virtual IEnumerable<XamlMember> LookupAllMembers ()
374 if (UnderlyingType == null)
375 return BaseType != null ? BaseType.GetAllMembers () : empty_array;
376 if (all_members_cache == null)
377 all_members_cache = new List<XamlMember> (DoLookupAllMembers ());
378 return all_members_cache;
381 List<XamlMember> all_members_cache;
383 IEnumerable<XamlMember> DoLookupAllMembers ()
385 // This is a hack that is likely required due to internal implementation difference in System.Uri. Our Uri has two readonly collection properties
386 if (this == XamlLanguage.Uri)
389 var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
391 foreach (var pi in UnderlyingType.GetProperties (bf))
392 if (pi.CanRead && (pi.CanWrite && pi.GetIndexParameters ().Length == 0 || IsCollectionType (pi.PropertyType)))
393 yield return new XamlMember (pi, SchemaContext);
394 foreach (var ei in UnderlyingType.GetEvents (bf))
395 yield return new XamlMember (ei, SchemaContext);
398 static bool IsCollectionType (Type type)
405 Type [] ifaces = type.GetInterfaces ();
406 foreach (Type i in ifaces)
407 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
409 foreach (Type i in ifaces)
410 if (i == typeof (IList))
413 foreach (var iface in type.GetInterfaces ())
414 if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
420 protected virtual IList<XamlType> LookupAllowedContentTypes ()
422 // the actual implementation is very different from what is documented :(
426 var l = new List<XamlType> ();
427 if (ContentWrappers != null)
428 l.AddRange (ContentWrappers);
429 if (ContentProperty != null)
430 l.Add (ContentProperty.Type);
431 if (ItemType != null)
433 return l.Count > 0 ? l : null;
437 protected virtual XamlMember LookupAttachableMember (string name)
439 throw new NotImplementedException ();
443 protected virtual XamlType LookupBaseType ()
445 if (base_type == null) {
446 if (UnderlyingType == null)
447 // FIXME: probably something advanced is needed here.
448 base_type = new XamlType (typeof (object), SchemaContext, Invoker);
450 base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
455 // This implementation is not verified. (No place to use.)
456 protected virtual XamlCollectionKind LookupCollectionKind ()
458 if (UnderlyingType == null)
459 return BaseType != null ? BaseType.LookupCollectionKind () : XamlCollectionKind.None;
461 return XamlCollectionKind.Array;
463 if (type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)))
464 return XamlCollectionKind.Dictionary;
466 if (type.ImplementsAnyInterfacesOf (typeof (ICollection), typeof (ICollection<>)))
467 return XamlCollectionKind.Collection;
469 return XamlCollectionKind.None;
472 protected virtual bool LookupConstructionRequiresArguments ()
474 if (UnderlyingType == null)
477 // not sure if it is required, but MemberDefinition return true while they are abstract and it makes no sense.
478 if (UnderlyingType.IsAbstract)
481 // FIXME: probably some primitive types are treated as special.
482 switch (Type.GetTypeCode (UnderlyingType)) {
483 case TypeCode.String:
485 case TypeCode.Object:
486 if (UnderlyingType == typeof (TimeSpan))
493 return UnderlyingType.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) == null;
496 protected virtual XamlMember LookupContentProperty ()
498 var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
499 return a != null && a.Name != null ? GetMember (a.Name) : null;
502 protected virtual IList<XamlType> LookupContentWrappers ()
504 if (CustomAttributeProvider == null)
507 var arr = CustomAttributeProvider.GetCustomAttributes (typeof (ContentWrapperAttribute), false);
508 if (arr == null || arr.Length == 0)
510 var l = new XamlType [arr.Length];
511 for (int i = 0; i < l.Length; i++)
512 l [i] = SchemaContext.GetXamlType (((ContentWrapperAttribute) arr [i]).ContentWrapper);
516 internal ICustomAttributeProvider CustomAttributeProvider {
517 get { return LookupCustomAttributeProvider (); }
520 protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
522 return UnderlyingType;
524 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
526 throw new NotImplementedException ();
529 protected virtual XamlTypeInvoker LookupInvoker ()
534 protected virtual bool LookupIsAmbient ()
536 return this.GetCustomAttribute<AmbientAttribute> () != null;
539 // It is documented as if it were to reflect spec. section 5.2,
540 // but the actual behavior shows it is *totally* wrong.
541 // Here I have implemented this based on the nunit test results. sigh.
542 protected virtual bool LookupIsConstructible ()
544 if (UnderlyingType == null)
546 if (IsMarkupExtension)
548 if (UnderlyingType.IsAbstract)
555 protected virtual bool LookupIsMarkupExtension ()
557 return typeof (MarkupExtension).IsAssignableFrom (UnderlyingType);
560 protected virtual bool LookupIsNameScope ()
562 return typeof (INameScope).IsAssignableFrom (UnderlyingType);
565 protected virtual bool LookupIsNullable ()
567 return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
570 protected virtual bool LookupIsPublic ()
572 return underlying_type == null || underlying_type.IsPublic || underlying_type.IsNestedPublic;
575 protected virtual bool LookupIsUnknown ()
577 return UnderlyingType == null;
580 protected virtual bool LookupIsWhitespaceSignificantCollection ()
582 // probably for unknown types, it should preserve whitespaces.
583 return IsUnknown || this.GetCustomAttribute<WhitespaceSignificantCollectionAttribute> () != null;
586 protected virtual bool LookupIsXData ()
588 // huh? XamlLanguage.XData.IsXData returns false(!)
589 // return typeof (XData).IsAssignableFrom (UnderlyingType);
593 protected virtual XamlType LookupItemType ()
596 return new XamlType (type.GetElementType (), SchemaContext);
599 return new XamlType (typeof (object), SchemaContext);
600 return new XamlType (type.GetGenericArguments () [1], SchemaContext);
605 return new XamlType (typeof (object), SchemaContext);
606 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
609 protected virtual XamlType LookupKeyType ()
614 return new XamlType (typeof (object), SchemaContext);
615 return new XamlType (type.GetGenericArguments () [0], SchemaContext);
618 protected virtual XamlType LookupMarkupExtensionReturnType ()
620 var a = this.GetCustomAttribute<MarkupExtensionReturnTypeAttribute> ();
621 return a != null ? new XamlType (a.ReturnType, SchemaContext) : null;
624 protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
626 // FIXME: verify if this does not filter out events.
627 return GetAllMembers ().FirstOrDefault (m => m.Name == name && (skipReadOnlyCheck || !m.IsReadOnly || m.Type.IsCollection || m.Type.IsDictionary || m.Type.IsArray));
630 protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
632 if (UnderlyingType == null/* || !IsMarkupExtension*/) // see nunit tests...
635 // check if there is applicable ConstructorArgumentAttribute.
636 // If there is, then return its type.
637 if (parameterCount == 1) {
638 foreach (var xm in GetAllMembers ()) {
639 var ca = xm.CustomAttributeProvider.GetCustomAttribute<ConstructorArgumentAttribute> (false);
641 return new XamlType [] {xm.Type};
645 var methods = (from m in UnderlyingType.GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) where m.GetParameters ().Length == parameterCount select m).ToArray ();
646 if (methods.Length == 1)
647 return (from p in methods [0].GetParameters () select SchemaContext.GetXamlType (p.ParameterType)).ToArray ();
649 if (SchemaContext.SupportMarkupExtensionsWithDuplicateArity)
650 throw new NotSupportedException ("The default LookupPositionalParameters implementation does not allow duplicate arity of markup extensions");
654 BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
656 protected virtual EventHandler<XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler ()
658 var a = this.GetCustomAttribute<XamlSetMarkupExtensionAttribute> ();
661 var mi = type.GetMethod (a.XamlSetMarkupExtensionHandler, flags_get_static);
663 throw new ArgumentException ("Binding to XamlSetMarkupExtensionHandler failed");
664 return (EventHandler<XamlSetMarkupExtensionEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetMarkupExtensionEventArgs>), mi);
667 protected virtual EventHandler<XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler ()
669 var a = this.GetCustomAttribute<XamlSetTypeConverterAttribute> ();
672 var mi = type.GetMethod (a.XamlSetTypeConverterHandler, flags_get_static);
674 throw new ArgumentException ("Binding to XamlSetTypeConverterHandler failed");
675 return (EventHandler<XamlSetTypeConverterEventArgs>) Delegate.CreateDelegate (typeof (EventHandler<XamlSetTypeConverterEventArgs>), mi);
678 protected virtual bool LookupTrimSurroundingWhitespace ()
680 return this.GetCustomAttribute<TrimSurroundingWhitespaceAttribute> () != null;
683 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
685 var t = UnderlyingType;
689 // equivalent to TypeExtension.
690 // FIXME: not sure if it should be specially handled here.
691 if (t == typeof (Type))
692 t = typeof (TypeExtension);
694 var a = CustomAttributeProvider.GetCustomAttribute<TypeConverterAttribute> (false);
696 return SchemaContext.GetValueConverter<TypeConverter> (Type.GetType (a.ConverterTypeName), this);
698 if (t == typeof (object))
699 return SchemaContext.GetValueConverter<TypeConverter> (typeof (TypeConverter), this);
701 // It's still not decent to check CollectionConverter.
702 var tct = TypeDescriptor.GetConverter (t).GetType ();
703 if (tct != typeof (TypeConverter) && tct != typeof (CollectionConverter) && tct != typeof (ReferenceConverter))
704 return SchemaContext.GetValueConverter<TypeConverter> (tct, this);
708 protected virtual Type LookupUnderlyingType ()
710 return underlying_type;
713 protected virtual bool LookupUsableDuringInitialization ()
715 var a = this.GetCustomAttribute<UsableDuringInitializationAttribute> ();
716 return a != null && a.Usable;
719 static XamlValueConverter<ValueSerializer> string_value_serializer;
721 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
723 return LookupValueSerializer (this, CustomAttributeProvider);
726 internal static XamlValueConverter<ValueSerializer> LookupValueSerializer (XamlType targetType, ICustomAttributeProvider provider)
728 if (provider == null)
731 var a = provider.GetCustomAttribute<ValueSerializerAttribute> (true);
733 return new XamlValueConverter<ValueSerializer> (a.ValueSerializerType ?? Type.GetType (a.ValueSerializerTypeName), targetType);
735 if (targetType.BaseType != null) {
736 var ret = targetType.BaseType.LookupValueSerializer ();
741 if (targetType.UnderlyingType == typeof (string)) {
742 if (string_value_serializer == null)
743 string_value_serializer = new XamlValueConverter<ValueSerializer> (typeof (StringValueSerializer), targetType);
744 return string_value_serializer;
750 internal IEnumerable<XamlMember> GetConstructorArguments ()
752 return GetAllMembers ().Where (m => m.UnderlyingMember != null && m.CustomAttributeProvider.GetCustomAttribute<ConstructorArgumentAttribute> (false) != null);