1697c0fdcc923cd508e4785f9355d2d873830abf
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / Binding.cs
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Globalization;
5 using System.Linq;
6 using System.Linq.Expressions;
7 using System.Reflection;
8 using System.Text;
9 using Tizen.NUI.Binding.Internals;
10
11 namespace Tizen.NUI.Binding
12 {
13     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
14     [EditorBrowsable(EditorBrowsableState.Never)]
15     public sealed class Binding : BindingBase
16     {
17         internal const string SelfPath = ".";
18         IValueConverter _converter;
19         object _converterParameter;
20
21         BindingExpression _expression;
22         string _path;
23         object _source;
24         string _updateSourceEventName;
25
26         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
27         [EditorBrowsable(EditorBrowsableState.Never)]
28         public Binding()
29         {
30         }
31
32         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
33         [EditorBrowsable(EditorBrowsableState.Never)]
34         public Binding(string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null, string stringFormat = null, object source = null)
35         {
36             if (path == null)
37                 throw new ArgumentNullException("path");
38             if (string.IsNullOrWhiteSpace(path))
39                 throw new ArgumentException("path can not be an empty string", "path");
40
41             Path = path;
42             Converter = converter;
43             ConverterParameter = converterParameter;
44             Mode = mode;
45             StringFormat = stringFormat;
46             Source = source;
47         }
48
49         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public IValueConverter Converter
52         {
53             get { return _converter; }
54             set
55             {
56                 ThrowIfApplied();
57
58                 _converter = value;
59             }
60         }
61
62         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
63         [EditorBrowsable(EditorBrowsableState.Never)]
64         public object ConverterParameter
65         {
66             get { return _converterParameter; }
67             set
68             {
69                 ThrowIfApplied();
70
71                 _converterParameter = value;
72             }
73         }
74
75         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
76         [EditorBrowsable(EditorBrowsableState.Never)]
77         public string Path
78         {
79             get { return _path; }
80             set
81             {
82                 ThrowIfApplied();
83
84                 _path = value;
85                 _expression = new BindingExpression(this, !string.IsNullOrWhiteSpace(value) ? value : SelfPath);
86             }
87         }
88
89         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
90         [EditorBrowsable(EditorBrowsableState.Never)]
91         public object Source
92         {
93             get { return _source; }
94             set
95             {
96                 ThrowIfApplied();
97                 _source = value;
98             }
99         }
100
101         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
102         [EditorBrowsable(EditorBrowsableState.Never)]
103         public string UpdateSourceEventName {
104             get { return _updateSourceEventName; }
105             set {
106                 ThrowIfApplied();
107                 _updateSourceEventName = value;
108             }
109         }
110
111         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
112         [EditorBrowsable(EditorBrowsableState.Never)]
113         [Obsolete]
114         public static Binding Create<TSource>(Expression<Func<TSource, object>> propertyGetter, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null,
115                                               string stringFormat = null)
116         {
117             if (propertyGetter == null)
118                 throw new ArgumentNullException("propertyGetter");
119
120                         return new Binding(GetBindingPath(propertyGetter), mode, converter, converterParameter, stringFormat);
121                 }
122
123         internal override void Apply(bool fromTarget)
124         {
125             base.Apply(fromTarget);
126
127             if (_expression == null)
128                 _expression = new BindingExpression(this, SelfPath);
129
130             _expression.Apply(fromTarget);
131         }
132
133         internal override void Apply(object newContext, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged = false)
134         {
135             object src = _source;
136             var isApplied = IsApplied;
137
138             base.Apply(src ?? newContext, bindObj, targetProperty, fromBindingContextChanged: fromBindingContextChanged);
139
140             if (src != null && isApplied && fromBindingContextChanged)
141                 return;
142
143             object bindingContext = src ?? Context ?? newContext;
144             if (_expression == null && bindingContext != null)
145                 _expression = new BindingExpression(this, SelfPath);
146
147             _expression?.Apply(bindingContext, bindObj, targetProperty);
148         }
149
150         internal override BindingBase Clone()
151         {
152             return new Binding(Path, Mode) { Converter = Converter, ConverterParameter = ConverterParameter, StringFormat = StringFormat, Source = Source, UpdateSourceEventName = UpdateSourceEventName };
153         }
154
155         internal override object GetSourceValue(object value, Type targetPropertyType)
156         {
157             if (Converter != null)
158                 value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
159
160             return base.GetSourceValue(value, targetPropertyType);
161         }
162
163         internal override object GetTargetValue(object value, Type sourcePropertyType)
164         {
165             if (Converter != null)
166                 value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
167
168             return base.GetTargetValue(value, sourcePropertyType);
169         }
170
171         internal override void Unapply(bool fromBindingContextChanged = false)
172         {
173             if (Source != null && fromBindingContextChanged && IsApplied)
174                 return;
175             
176             base.Unapply(fromBindingContextChanged: fromBindingContextChanged);
177
178             if (_expression != null)
179                 _expression.Unapply();
180         }
181
182         [Obsolete]
183         static string GetBindingPath<TSource>(Expression<Func<TSource, object>> propertyGetter)
184         {
185             Expression expr = propertyGetter.Body;
186
187             var unary = expr as UnaryExpression;
188             if (unary != null)
189                 expr = unary.Operand;
190
191             var builder = new StringBuilder();
192
193             var indexed = false;
194
195             var member = expr as MemberExpression;
196             if (member == null)
197             {
198                 var methodCall = expr as MethodCallExpression;
199                 if (methodCall != null)
200                 {
201                     if (methodCall.Arguments.Count == 0)
202                         throw new ArgumentException("Method calls are not allowed in binding expression");
203
204                     var arguments = new List<string>(methodCall.Arguments.Count);
205                     foreach (Expression arg in methodCall.Arguments)
206                     {
207                         if (arg.NodeType != ExpressionType.Constant)
208                             throw new ArgumentException("Only constants can be used as indexer arguments");
209
210                         object value = ((ConstantExpression)arg).Value;
211                         arguments.Add(value != null ? value.ToString() : "null");
212                     }
213
214                     Type declarerType = methodCall.Method.DeclaringType;
215                     DefaultMemberAttribute defaultMember = declarerType.GetTypeInfo().GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType<DefaultMemberAttribute>().FirstOrDefault();
216                     string indexerName = defaultMember != null ? defaultMember.MemberName : "Item";
217
218                     MethodInfo getterInfo =
219                         declarerType.GetProperties().Where(pi => (pi.GetMethod != null) && pi.Name == indexerName && pi.CanRead && pi.GetMethod.IsPublic && !pi.GetMethod.IsStatic).Select(pi => pi.GetMethod).FirstOrDefault();
220                     if (getterInfo != null)
221                     {
222                         if (getterInfo == methodCall.Method)
223                         {
224                             indexed = true;
225                             builder.Append("[");
226
227                             var first = true;
228                             foreach (string argument in arguments)
229                             {
230                                 if (!first)
231                                     builder.Append(",");
232
233                                 builder.Append(argument);
234                                 first = false;
235                             }
236
237                             builder.Append("]");
238
239                             member = methodCall.Object as MemberExpression;
240                         }
241                         else
242                             throw new ArgumentException("Method calls are not allowed in binding expressions");
243                     }
244                     else
245                         throw new ArgumentException("Public indexer not found");
246                 }
247                 else
248                     throw new ArgumentException("Invalid expression type");
249             }
250
251             while (member != null)
252             {
253                 var property = (PropertyInfo)member.Member;
254                 if (builder.Length != 0)
255                 {
256                     if (!indexed)
257                         builder.Insert(0, ".");
258                     else
259                         indexed = false;
260                 }
261
262                 builder.Insert(0, property.Name);
263
264                 //                              member = member.Expression as MemberExpression ?? (member.Expression as UnaryExpression)?.Operand as MemberExpression;
265                 member = member.Expression as MemberExpression ?? (member.Expression is UnaryExpression ? (member.Expression as UnaryExpression).Operand as MemberExpression : null);
266             }
267
268             return builder.ToString();
269         }
270     }
271 }