[NUI]Add xaml support for nui and nui xaml test sample (#230)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / XamlBinding / BindablePropertyConverter.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Globalization;
5 using System.Linq;
6 using System.Reflection;
7 using System.Xml;
8 using Tizen.NUI.Internals;
9 using Tizen.NUI.Xaml;
10
11 namespace Tizen.NUI.Binding
12 {
13         // [Xaml.ProvideCompiled("Xamarin.Forms.Core.XamlC.BindablePropertyConverter")]
14         [Xaml.TypeConversion(typeof(BindableProperty))]
15         internal sealed class BindablePropertyConverter : TypeConverter, IExtendedTypeConverter
16         {
17                 object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
18                 {
19                         return ((IExtendedTypeConverter)this).ConvertFromInvariantString(value as string, serviceProvider);
20                 }
21
22                 object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
23                 {
24                         if (string.IsNullOrWhiteSpace(value))
25                                 return null;
26                         if (serviceProvider == null)
27                                 return null;
28                         var parentValuesProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideParentValues;
29                         var typeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
30                         if (typeResolver == null)
31                                 return null;
32                         IXmlLineInfo lineinfo = null;
33                         var xmlLineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
34                         if (xmlLineInfoProvider != null)
35                                 lineinfo = xmlLineInfoProvider.XmlLineInfo;
36                         string[] parts = value.Split('.');
37                         Type type = null;
38                         if (parts.Length == 1)
39                         {
40                                 if (parentValuesProvider == null)
41                                 {
42                                         string msg = string.Format("Can't resolve {0}", parts[0]);
43                                         throw new XamlParseException(msg, lineinfo);
44                                 }
45                                 object parent = parentValuesProvider.ParentObjects.Skip(1).FirstOrDefault();
46                                 if (parentValuesProvider.TargetObject is Setter)
47                                 {
48                                         var style = parent as Style;
49                                         var triggerBase = parent as TriggerBase;
50                                         var visualState = parent as VisualState;
51                                         if (style != null)
52                                                 type = style.TargetType;
53                                         else if (triggerBase != null)
54                                                 type = triggerBase.TargetType;
55                                         else if (visualState != null)
56                                                 type = FindTypeForVisualState(parentValuesProvider, lineinfo);
57                                 }
58                                 else if (parentValuesProvider.TargetObject is Trigger)
59                                         type = (parentValuesProvider.TargetObject as Trigger).TargetType;
60                                 else if (parentValuesProvider.TargetObject is PropertyCondition && (parent as TriggerBase) != null)
61                                         type = (parent as TriggerBase).TargetType;
62
63                                 if (type == null)
64                                         throw new XamlParseException($"Can't resolve {parts [0]}", lineinfo);
65
66                                 return ConvertFrom(type, parts[0], lineinfo);
67                         }
68                         if (parts.Length == 2)
69                         {
70                                 if (!typeResolver.TryResolve(parts[0], out type))
71                                 {
72                                         string msg = string.Format("Can't resolve {0}", parts[0]);
73                                         throw new XamlParseException(msg, lineinfo);
74                                 }
75                                 return ConvertFrom(type, parts[1], lineinfo);
76                         }
77                         throw new XamlParseException($"Can't resolve {value}. Syntax is [[prefix:]Type.]PropertyName.", lineinfo);
78                 }
79
80                 public override object ConvertFromInvariantString(string value)
81                 {
82                         if (string.IsNullOrWhiteSpace(value))
83                                 return null;
84                         if (value.Contains(":"))
85                         {
86                                 Console.WriteLine(null, "Can't resolve properties with xml namespace prefix.");
87                                 return null;
88                         }
89                         string[] parts = value.Split('.');
90                         if (parts.Length != 2)
91                         {
92                                 Console.WriteLine(null, $"Can't resolve {value}. Accepted syntax is Type.PropertyName.");
93                                 return null;
94                         }
95                         Type type = Type.GetType("Tizen.NUI." + parts[0]);
96                         return ConvertFrom(type, parts[1], null);
97                 }
98
99                 BindableProperty ConvertFrom(Type type, string propertyName, IXmlLineInfo lineinfo)
100                 {
101                         string name = propertyName + "Property";
102                         FieldInfo bpinfo = type.GetField(fi => fi.Name == name && fi.IsStatic && fi.IsPublic && fi.FieldType == typeof(BindableProperty));
103                         if (bpinfo == null)
104                                 throw new XamlParseException($"Can't resolve {name} on {type.Name}", lineinfo);
105                         var bp = bpinfo.GetValue(null) as BindableProperty;
106                         var isObsolete = bpinfo.GetCustomAttribute<ObsoleteAttribute>() != null;
107                         if (bp.PropertyName != propertyName && !isObsolete)
108                                 throw new XamlParseException($"The PropertyName of {type.Name}.{name} is not {propertyName}", lineinfo);
109                         return bp;
110                 }
111
112                 Type FindTypeForVisualState(IProvideParentValues parentValueProvider, IXmlLineInfo lineInfo)
113                 {
114                         var parents = parentValueProvider.ParentObjects.ToList();
115
116                         // Skip 0; we would not be making this check if TargetObject were not a Setter
117                         // Skip 1; we would not be making this check if the immediate parent were not a VisualState
118
119                         // VisualStates must be in a VisualStateGroup
120                         if(!(parents[2] is VisualStateGroup)) {
121                                 throw new XamlParseException($"Expected {nameof(VisualStateGroup)} but found {parents[2]}.", lineInfo);
122                         }
123
124                         var vsTarget = parents[3];
125
126                         // Are these Visual States directly on a VisualElement?
127                         if (vsTarget is /*VisualElement*/BaseHandle)
128                         {
129                                 return vsTarget.GetType();
130                         }
131
132                         if (!(parents[3] is VisualStateGroupList))
133                         {
134                                 throw new XamlParseException($"Expected {nameof(VisualStateGroupList)} but found {parents[3]}.", lineInfo);
135                         }
136
137                         if (!(parents[4] is Setter))
138                         {
139                                 throw new XamlParseException($"Expected {nameof(Setter)} but found {parents[4]}.", lineInfo);
140                         }
141
142                         // These must be part of a Style; verify that 
143                         if (!(parents[5] is Style style))
144                         {
145                                 throw new XamlParseException($"Expected {nameof(Style)} but found {parents[5]}.", lineInfo);
146                         }
147
148                         return style.TargetType;
149                 }
150         }
151 }