using System.Reflection;
using System.Windows.Markup;
using System.Xaml.Schema;
+using System.Xml.Serialization;
namespace System.Xaml
{
{
}
- static readonly Type [] predefined_types = {
- typeof (XData), typeof (Uri), typeof (TimeSpan), typeof (PropertyDefinition), typeof (MemberDefinition), typeof (Reference)
- };
+// static readonly Type [] predefined_types = {
+// typeof (XData), typeof (Uri), typeof (TimeSpan), typeof (PropertyDefinition), typeof (MemberDefinition), typeof (Reference)
+// };
public XamlType (Type underlyingType, XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
: this (schemaContext, invoker)
type = underlyingType;
underlying_type = type;
- Name = type.GetXamlName ();
- // FIXME: remove this hack
- if (Type.GetTypeCode (type) == TypeCode.Object && type != typeof (object)) {
- if (predefined_types.Contains (type) || typeof (MarkupExtension).IsAssignableFrom (type) && type.Assembly == typeof (XamlType).Assembly)
- PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
+ XamlType xt;
+ if (XamlLanguage.InitializingTypes) {
+ // These are special. Only XamlLanguage members are with shorthand name.
+ if (type == typeof (PropertyDefinition))
+ Name = "Property";
+ else if (type == typeof (MemberDefinition))
+ Name = "Member";
else
- PreferredXamlNamespace = String.Format ("clr-namespace:{0};assembly={1}", type.Namespace, type.Assembly.GetName ().Name);
- }
- else
+ Name = GetXamlName (type);
PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
+ } else if ((xt = XamlLanguage.AllTypes.FirstOrDefault (t => t.UnderlyingType == type)) != null) {
+ Name = xt.Name;
+ PreferredXamlNamespace = XamlLanguage.Xaml2006Namespace;
+ } else {
+ Name = GetXamlName (type);
+ PreferredXamlNamespace = schemaContext.GetXamlNamespace (type.Namespace) ?? String.Format ("clr-namespace:{0};assembly={1}", type.Namespace, type.Assembly.GetName ().Name);
+ }
+ if (type.IsGenericType) {
+ TypeArguments = new List<XamlType> ();
+ foreach (var gta in type.GetGenericArguments ())
+ TypeArguments.Add (schemaContext.GetXamlType (gta));
+ }
}
public XamlType (string unknownTypeNamespace, string unknownTypeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
Name = unknownTypeName;
PreferredXamlNamespace = unknownTypeNamespace;
TypeArguments = typeArguments != null && typeArguments.Count == 0 ? null : typeArguments;
+// explicit_ns = unknownTypeNamespace;
}
protected XamlType (string typeName, IList<XamlType> typeArguments, XamlSchemaContext schemaContext)
Type type, underlying_type;
+// string explicit_ns;
+
// populated properties
XamlType base_type;
XamlTypeInvoker invoker;
public bool IsAmbient {
get { return LookupIsAmbient (); }
}
+
public bool IsArray {
- get { return type.IsArray; }
+ get { return LookupCollectionKind () == XamlCollectionKind.Array; }
}
+
+ // it somehow treats array as not a collection...
public bool IsCollection {
- // it somehow treats array as not a collection...
- get { return !type.IsArray && type.ImplementsAnyInterfacesOf (typeof (ICollection), typeof (ICollection<>)); }
+ get { return LookupCollectionKind () == XamlCollectionKind.Collection; }
}
public bool IsConstructible {
}
public bool IsDictionary {
- get { return type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)); }
+ get { return LookupCollectionKind () == XamlCollectionKind.Dictionary; }
}
public bool IsGeneric {
get { return LookupValueSerializer (); }
}
+ internal string GetInternalXmlName ()
+ {
+ if (IsMarkupExtension && Name.EndsWith ("Extension", StringComparison.Ordinal))
+ return Name.Substring (0, Name.Length - 9);
+ var stn = XamlLanguage.SpecialNames.FirstOrDefault (s => s.Type == this);
+ return stn != null ? stn.Name : Name;
+ }
+
public static bool operator == (XamlType left, XamlType right)
{
return IsNull (left) ? IsNull (right) : left.Equals (right);
public bool Equals (XamlType other)
{
+ // It does not compare XamlSchemaContext.
return !IsNull (other) &&
UnderlyingType == other.UnderlyingType &&
Name == other.Name &&
- PreferredXamlNamespace == other.PreferredXamlNamespace &&
- CompareTypes (TypeArguments, other.TypeArguments);
- }
-
- static bool CompareTypes (IList<XamlType> a1, IList<XamlType> a2)
- {
- if (a1 == null)
- return a2 == null;
- if (a2 == null)
- return false;
- if (a1.Count != a2.Count)
- return false;
- for (int i = 0; i < a1.Count; i++)
- if (a1 [i] != a2 [i])
- return false;
- return true;
+ PreferredXamlNamespace == other.PreferredXamlNamespace && TypeArguments.ListEquals (other.TypeArguments);
}
public override bool Equals (object obj)
public override string ToString ()
{
- return UnderlyingType != null ? UnderlyingType.ToString () : Name;
+ return new XamlTypeName (this).ToString ();
+ //return String.IsNullOrEmpty (PreferredXamlNamespace) ? Name : String.Concat ("{", PreferredXamlNamespace, "}", Name);
}
public virtual bool CanAssignTo (XamlType xamlType)
{
- throw new NotImplementedException ();
+ if (this.UnderlyingType == null)
+ return xamlType == XamlLanguage.Object;
+ var ut = xamlType.UnderlyingType ?? typeof (object);
+ return ut.IsAssignableFrom (UnderlyingType);
}
public XamlMember GetAliasedProperty (XamlDirective directive)
public XamlMember GetMember (string name)
{
- return LookupMember (name, false);
+ return LookupMember (name, true);
}
public IList<XamlType> GetPositionalParameters (int parameterCount)
public virtual IList<string> GetXamlNamespaces ()
{
throw new NotImplementedException ();
+ /* this does not work like documented!
+ if (explicit_ns != null)
+ return new string [] {explicit_ns};
+ var l = SchemaContext.GetAllXamlNamespaces ();
+ if (l != null)
+ return new List<string> (l);
+ return new string [] {String.Empty};
+ */
}
// lookups
protected virtual XamlMember LookupAliasedProperty (XamlDirective directive)
{
- throw new NotImplementedException ();
+ if (directive == XamlLanguage.Key) {
+ var a = this.GetCustomAttribute<DictionaryKeyPropertyAttribute> ();
+ return a != null ? GetMember (a.Name) : null;
+ }
+ if (directive == XamlLanguage.Name) {
+ var a = this.GetCustomAttribute<RuntimeNamePropertyAttribute> ();
+ return a != null ? GetMember (a.Name) : null;
+ }
+ if (directive == XamlLanguage.Uid) {
+ var a = this.GetCustomAttribute<UidPropertyAttribute> ();
+ return a != null ? GetMember (a.Name) : null;
+ }
+ if (directive == XamlLanguage.Lang) {
+ var a = this.GetCustomAttribute<XmlLangPropertyAttribute> ();
+ return a != null ? GetMember (a.Name) : null;
+ }
+ return null;
}
+
protected virtual IEnumerable<XamlMember> LookupAllAttachableMembers ()
{
- throw new NotImplementedException ();
+ if (UnderlyingType == null)
+ return BaseType != null ? BaseType.GetAllAttachableMembers () : empty_array;
+ if (all_attachable_members_cache == null) {
+ all_attachable_members_cache = new List<XamlMember> (DoLookupAllAttachableMembers ());
+ all_attachable_members_cache.Sort (TypeExtensionMethods.CompareMembers);
+ }
+ return all_attachable_members_cache;
+ }
+
+ IEnumerable<XamlMember> DoLookupAllAttachableMembers ()
+ {
+ // based on http://msdn.microsoft.com/en-us/library/ff184560.aspx
+ var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
+
+ var gl = new Dictionary<string,MethodInfo> ();
+ var sl = new Dictionary<string,MethodInfo> ();
+ var al = new Dictionary<string,MethodInfo> ();
+ //var rl = new Dictionary<string,MethodInfo> ();
+ var nl = new List<string> ();
+ foreach (var mi in UnderlyingType.GetMethods (bf)) {
+ string name = null;
+ if (mi.Name.StartsWith ("Get", StringComparison.Ordinal)) {
+ if (mi.ReturnType == typeof (void))
+ continue;
+ var args = mi.GetParameters ();
+ if (args.Length != 1)
+ continue;
+ name = mi.Name.Substring (3);
+ gl.Add (name, mi);
+ } else if (mi.Name.StartsWith ("Set", StringComparison.Ordinal)) {
+ // looks like the return type is *ignored*
+ //if (mi.ReturnType != typeof (void))
+ // continue;
+ var args = mi.GetParameters ();
+ if (args.Length != 2)
+ continue;
+ name = mi.Name.Substring (3);
+ sl.Add (name, mi);
+ } else if (mi.Name.EndsWith ("Handler", StringComparison.Ordinal)) {
+ var args = mi.GetParameters ();
+ if (args.Length != 2)
+ continue;
+ if (mi.Name.StartsWith ("Add", StringComparison.Ordinal)) {
+ name = mi.Name.Substring (3, mi.Name.Length - 3 - 7);
+ al.Add (name, mi);
+ }/* else if (mi.Name.StartsWith ("Remove", StringComparison.Ordinal)) {
+ name = mi.Name.Substring (6, mi.Name.Length - 6 - 7);
+ rl.Add (name, mi);
+ }*/
+ }
+ if (name != null && !nl.Contains (name))
+ nl.Add (name);
+ }
+
+ foreach (var name in nl) {
+ MethodInfo m;
+ var g = gl.TryGetValue (name, out m) ? m : null;
+ var s = sl.TryGetValue (name, out m) ? m : null;
+ if (g != null || s != null)
+ yield return new XamlMember (name, g, s, SchemaContext);
+ var a = al.TryGetValue (name, out m) ? m : null;
+ //var r = rl.TryGetValue (name, out m) ? m : null;
+ if (a != null)
+ yield return new XamlMember (name, a, SchemaContext);
+ }
}
+
+ static readonly XamlMember [] empty_array = new XamlMember [0];
+
protected virtual IEnumerable<XamlMember> LookupAllMembers ()
{
- throw new NotImplementedException ();
+ if (UnderlyingType == null)
+ return BaseType != null ? BaseType.GetAllMembers () : empty_array;
+ if (all_members_cache == null) {
+ all_members_cache = new List<XamlMember> (DoLookupAllMembers ());
+ all_members_cache.Sort (TypeExtensionMethods.CompareMembers);
+ }
+ return all_members_cache;
+ }
+
+ List<XamlMember> all_members_cache;
+ List<XamlMember> all_attachable_members_cache;
+
+ IEnumerable<XamlMember> DoLookupAllMembers ()
+ {
+ // This is a hack that is likely required due to internal implementation difference in System.Uri. Our Uri has two readonly collection properties
+ if (this == XamlLanguage.Uri)
+ yield break;
+
+ var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+
+ foreach (var pi in UnderlyingType.GetProperties (bf)) {
+ if (pi.Name.Contains ('.')) // exclude explicit interface implementations.
+ continue;
+ if (pi.CanRead && (pi.CanWrite || IsCollectionType (pi.PropertyType) || typeof (IXmlSerializable).IsAssignableFrom (pi.PropertyType)) && pi.GetIndexParameters ().Length == 0)
+ yield return new XamlMember (pi, SchemaContext);
+ }
+ foreach (var ei in UnderlyingType.GetEvents (bf))
+ yield return new XamlMember (ei, SchemaContext);
}
+
+ static bool IsPublicAccessor (MethodInfo mi)
+ {
+ return mi != null && mi.IsPublic;
+ }
+
+ bool IsCollectionType (Type type)
+ {
+ if (type == null)
+ return false;
+ var xt = SchemaContext.GetXamlType (type);
+ return xt.LookupCollectionKind () != XamlCollectionKind.None;
+ }
+
protected virtual IList<XamlType> LookupAllowedContentTypes ()
{
- throw new NotImplementedException ();
+ // the actual implementation is very different from what is documented :(
+ return null;
+
+ /*
+ var l = new List<XamlType> ();
+ if (ContentWrappers != null)
+ l.AddRange (ContentWrappers);
+ if (ContentProperty != null)
+ l.Add (ContentProperty.Type);
+ if (ItemType != null)
+ l.Add (ItemType);
+ return l.Count > 0 ? l : null;
+ */
}
+
protected virtual XamlMember LookupAttachableMember (string name)
{
- throw new NotImplementedException ();
+ return GetAllAttachableMembers ().FirstOrDefault (m => m.Name == name);
}
- [MonoTODO]
protected virtual XamlType LookupBaseType ()
{
if (base_type == null) {
if (UnderlyingType == null)
- // FIXME: probably something advanced is needed here.
- base_type = new XamlType (typeof (object), SchemaContext, Invoker);
+ base_type = SchemaContext.GetXamlType (typeof (object));
else
- base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : new XamlType (type.BaseType, SchemaContext, Invoker);
+ base_type = type.BaseType == null || type.BaseType == typeof (object) ? null : SchemaContext.GetXamlType (type.BaseType);
}
return base_type;
}
- protected virtual XamlCollectionKind LookupCollectionKind ()
+ // This implementation is not verified. (No place to use.)
+ protected internal virtual XamlCollectionKind LookupCollectionKind ()
{
- throw new NotImplementedException ();
+ if (UnderlyingType == null)
+ return BaseType != null ? BaseType.LookupCollectionKind () : XamlCollectionKind.None;
+ if (type.IsArray)
+ return XamlCollectionKind.Array;
+
+ if (type.ImplementsAnyInterfacesOf (typeof (IDictionary), typeof (IDictionary<,>)))
+ return XamlCollectionKind.Dictionary;
+
+ if (type.ImplementsAnyInterfacesOf (typeof (IList), typeof (ICollection<>)))
+ return XamlCollectionKind.Collection;
+
+ return XamlCollectionKind.None;
}
protected virtual bool LookupConstructionRequiresArguments ()
if (UnderlyingType == null)
return false;
- // not sure if it is required, but TypeDefinition and MemberDefinition return true while they are abstract and it makes no sense.
+ // not sure if it is required, but MemberDefinition return true while they are abstract and it makes no sense.
if (UnderlyingType.IsAbstract)
return true;
var a = this.GetCustomAttribute<ContentPropertyAttribute> ();
return a != null && a.Name != null ? GetMember (a.Name) : null;
}
+
protected virtual IList<XamlType> LookupContentWrappers ()
{
- throw new NotImplementedException ();
+ if (GetCustomAttributeProvider () == null)
+ return null;
+
+ var arr = GetCustomAttributeProvider ().GetCustomAttributes (typeof (ContentWrapperAttribute), false);
+ if (arr == null || arr.Length == 0)
+ return null;
+ var l = new XamlType [arr.Length];
+ for (int i = 0; i < l.Length; i++)
+ l [i] = SchemaContext.GetXamlType (((ContentWrapperAttribute) arr [i]).ContentWrapper);
+ return l;
}
- protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
+
+ internal ICustomAttributeProvider GetCustomAttributeProvider ()
{
- throw new NotImplementedException ();
+ return LookupCustomAttributeProvider ();
}
+
+ protected internal virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
+ {
+ return UnderlyingType;
+ }
+
protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
{
throw new NotImplementedException ();
return this.GetCustomAttribute<AmbientAttribute> () != null;
}
+ // It is documented as if it were to reflect spec. section 5.2,
+ // but the actual behavior shows it is *totally* wrong.
+ // Here I have implemented this based on the nunit test results. sigh.
protected virtual bool LookupIsConstructible ()
{
- // see spec. 5.2.
- if (IsArray) // x:Array
- return false;
- if (type == typeof (XamlType)) // x:XamlType
- return false;
- // FIXME: handle x:XamlEvent
+ if (UnderlyingType == null)
+ return true;
if (IsMarkupExtension)
+ return true;
+ if (UnderlyingType.IsAbstract)
return false;
- // FIXME: handle x:Code
- // FIXME: commented out.
- //if (IsXData)
- // return false;
-
- // FIXME: this check is extraneous to spec. 5.2.
- if (ConstructionRequiresArguments)
+ if (!IsNameValid)
return false;
-
return true;
}
protected virtual bool LookupIsNullable ()
{
- return !type.IsValueType || type.ImplementsInterface (typeof (Nullable<>));
+ return !type.IsValueType || type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>);
}
protected virtual bool LookupIsPublic ()
protected virtual bool LookupIsXData ()
{
- // huh? XamlLanguage.XData.IsXData returns false(!)
- // return typeof (XData).IsAssignableFrom (UnderlyingType);
- return false;
+ return CanAssignTo (SchemaContext.GetXamlType (typeof (IXmlSerializable)));
}
protected virtual XamlType LookupItemType ()
{
if (IsArray)
return new XamlType (type.GetElementType (), SchemaContext);
+ if (IsDictionary) {
+ if (!IsGeneric)
+ return new XamlType (typeof (object), SchemaContext);
+ return new XamlType (type.GetGenericArguments () [1], SchemaContext);
+ }
if (!IsCollection)
return null;
if (!IsGeneric)
protected virtual XamlMember LookupMember (string name, bool skipReadOnlyCheck)
{
- var pi = UnderlyingType.GetProperty (name);
- if (pi != null && (skipReadOnlyCheck || pi.CanWrite))
- return new XamlMember (pi, SchemaContext);
- var ei = UnderlyingType.GetEvent (name);
- if (ei != null)
- return new XamlMember (ei, SchemaContext);
- return null;
+ // FIXME: verify if this does not filter out events.
+ return GetAllMembers ().FirstOrDefault (m => m.Name == name && (skipReadOnlyCheck || !m.IsReadOnly || m.Type.IsCollection || m.Type.IsDictionary || m.Type.IsArray));
}
protected virtual IList<XamlType> LookupPositionalParameters (int parameterCount)
{
- throw new NotImplementedException ();
+ if (UnderlyingType == null/* || !IsMarkupExtension*/) // see nunit tests...
+ return null;
+
+ // check if there is applicable ConstructorArgumentAttribute.
+ // If there is, then return its type.
+ if (parameterCount == 1) {
+ foreach (var xm in GetAllMembers ()) {
+ var ca = xm.GetCustomAttributeProvider ().GetCustomAttribute<ConstructorArgumentAttribute> (false);
+ if (ca != null)
+ return new XamlType [] {xm.Type};
+ }
+ }
+
+ var methods = (from m in UnderlyingType.GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) where m.GetParameters ().Length == parameterCount select m).ToArray ();
+ if (methods.Length == 1)
+ return (from p in methods [0].GetParameters () select SchemaContext.GetXamlType (p.ParameterType)).ToArray ();
+
+ if (SchemaContext.SupportMarkupExtensionsWithDuplicateArity)
+ throw new NotSupportedException ("The default LookupPositionalParameters implementation does not allow duplicate arity of markup extensions");
+ return null;
}
BindingFlags flags_get_static = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
{
- throw new NotImplementedException ();
+ var t = UnderlyingType;
+ if (t == null)
+ return null;
+
+ // equivalent to TypeExtension.
+ // FIXME: not sure if it should be specially handled here.
+ if (t == typeof (Type))
+ t = typeof (TypeExtension);
+
+ var a = GetCustomAttributeProvider ();
+ var ca = a != null ? a.GetCustomAttribute<TypeConverterAttribute> (false) : null;
+ if (ca != null)
+ return SchemaContext.GetValueConverter<TypeConverter> (Type.GetType (ca.ConverterTypeName), this);
+
+ if (t == typeof (object)) // This is a special case. ConverterType is null.
+ return SchemaContext.GetValueConverter<TypeConverter> (null, this);
+
+ // It's still not decent to check CollectionConverter.
+ var tct = t.GetTypeConverter ().GetType ();
+ if (tct != typeof (TypeConverter) && tct != typeof (CollectionConverter) && tct != typeof (ReferenceConverter))
+ return SchemaContext.GetValueConverter<TypeConverter> (tct, this);
+ return null;
}
protected virtual Type LookupUnderlyingType ()
return a != null && a.Usable;
}
+ static XamlValueConverter<ValueSerializer> string_value_serializer;
+
protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
{
- throw new NotImplementedException ();
+ return LookupValueSerializer (this, GetCustomAttributeProvider ());
+ }
+
+ internal static XamlValueConverter<ValueSerializer> LookupValueSerializer (XamlType targetType, ICustomAttributeProvider provider)
+ {
+ if (provider == null)
+ return null;
+
+ var a = provider.GetCustomAttribute<ValueSerializerAttribute> (true);
+ if (a != null)
+ return new XamlValueConverter<ValueSerializer> (a.ValueSerializerType ?? Type.GetType (a.ValueSerializerTypeName), targetType);
+
+ if (targetType.BaseType != null) {
+ var ret = targetType.BaseType.LookupValueSerializer ();
+ if (ret != null)
+ return ret;
+ }
+
+ if (targetType.UnderlyingType == typeof (string)) {
+ if (string_value_serializer == null)
+ string_value_serializer = new XamlValueConverter<ValueSerializer> (typeof (StringValueSerializer), targetType);
+ return string_value_serializer;
+ }
+
+ return null;
+ }
+
+ static string GetXamlName (Type type)
+ {
+ string n;
+ if (!type.IsNestedPublic && !type.IsNestedAssembly && !type.IsNestedPrivate)
+ n = type.Name;
+ else
+ n = GetXamlName (type.DeclaringType) + "+" + type.Name;
+ if (type.IsGenericType && !type.ContainsGenericParameters) // the latter condition is to filter out "nested non-generic type within generic type".
+ return n.Substring (0, n.IndexOf ('`'));
+ else
+ return n;
}
}
}