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