Follow formatting NUI
[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(nameof(path));
38             if (string.IsNullOrWhiteSpace(path))
39                 throw new ArgumentException("path can not be an empty string", nameof(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         {
105             get { return _updateSourceEventName; }
106             set
107             {
108                 ThrowIfApplied();
109                 _updateSourceEventName = value;
110             }
111         }
112
113         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
114         [EditorBrowsable(EditorBrowsableState.Never)]
115         [Obsolete]
116         public static Binding Create<TSource>(Expression<Func<TSource, object>> propertyGetter, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null,
117                                               string stringFormat = null)
118         {
119             if (propertyGetter == null)
120                 throw new ArgumentNullException(nameof(propertyGetter));
121
122             return new Binding(GetBindingPath(propertyGetter), mode, converter, converterParameter, stringFormat);
123         }
124
125         internal override void Apply(bool fromTarget)
126         {
127             base.Apply(fromTarget);
128
129             if (_expression == null)
130                 _expression = new BindingExpression(this, SelfPath);
131
132             _expression.Apply(fromTarget);
133         }
134
135         internal override void Apply(object newContext, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged = false)
136         {
137             object src = _source;
138             var isApplied = IsApplied;
139
140             base.Apply(src ?? newContext, bindObj, targetProperty, fromBindingContextChanged: fromBindingContextChanged);
141
142             if (src != null && isApplied && fromBindingContextChanged)
143                 return;
144
145             object bindingContext = src ?? Context ?? newContext;
146             if (_expression == null && bindingContext != null)
147                 _expression = new BindingExpression(this, SelfPath);
148
149             _expression?.Apply(bindingContext, bindObj, targetProperty);
150         }
151
152         internal override BindingBase Clone()
153         {
154             return new Binding(Path, Mode) { Converter = Converter, ConverterParameter = ConverterParameter, StringFormat = StringFormat, Source = Source, UpdateSourceEventName = UpdateSourceEventName };
155         }
156
157         internal override object GetSourceValue(object value, Type targetPropertyType)
158         {
159             if (Converter != null)
160                 value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
161
162             return base.GetSourceValue(value, targetPropertyType);
163         }
164
165         internal override object GetTargetValue(object value, Type sourcePropertyType)
166         {
167             if (Converter != null)
168                 value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
169
170             return base.GetTargetValue(value, sourcePropertyType);
171         }
172
173         internal override void Unapply(bool fromBindingContextChanged = false)
174         {
175             if (Source != null && fromBindingContextChanged && IsApplied)
176                 return;
177
178             base.Unapply(fromBindingContextChanged: fromBindingContextChanged);
179
180             if (_expression != null)
181                 _expression.Unapply();
182         }
183
184         [Obsolete]
185         static string GetBindingPath<TSource>(Expression<Func<TSource, object>> propertyGetter)
186         {
187             Expression expr = propertyGetter.Body;
188
189             var unary = expr as UnaryExpression;
190             if (unary != null)
191                 expr = unary.Operand;
192
193             var builder = new StringBuilder();
194
195             var indexed = false;
196
197             var member = expr as MemberExpression;
198             if (member == null)
199             {
200                 var methodCall = expr as MethodCallExpression;
201                 if (methodCall != null)
202                 {
203                     if (methodCall.Arguments.Count == 0)
204                         throw new ArgumentException("Method calls are not allowed in binding expression");
205
206                     var arguments = new List<string>(methodCall.Arguments.Count);
207                     foreach (Expression arg in methodCall.Arguments)
208                     {
209                         if (arg.NodeType != ExpressionType.Constant)
210                             throw new ArgumentException("Only constants can be used as indexer arguments");
211
212                         object value = ((ConstantExpression)arg).Value;
213                         arguments.Add(value != null ? value.ToString() : "null");
214                     }
215
216                     Type declarerType = methodCall.Method.DeclaringType;
217                     DefaultMemberAttribute defaultMember = declarerType.GetTypeInfo().GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType<DefaultMemberAttribute>().FirstOrDefault();
218                     string indexerName = defaultMember != null ? defaultMember.MemberName : "Item";
219
220                     MethodInfo getterInfo =
221                         declarerType.GetProperties().Where(pi => (pi.GetMethod != null) && pi.Name == indexerName && pi.CanRead && pi.GetMethod.IsPublic && !pi.GetMethod.IsStatic).Select(pi => pi.GetMethod).FirstOrDefault();
222                     if (getterInfo != null)
223                     {
224                         if (getterInfo == methodCall.Method)
225                         {
226                             indexed = true;
227                             builder.Append("[");
228
229                             var first = true;
230                             foreach (string argument in arguments)
231                             {
232                                 if (!first)
233                                     builder.Append(",");
234
235                                 builder.Append(argument);
236                                 first = false;
237                             }
238
239                             builder.Append("]");
240
241                             member = methodCall.Object as MemberExpression;
242                         }
243                         else
244                             throw new ArgumentException("Method calls are not allowed in binding expressions");
245                     }
246                     else
247                         throw new ArgumentException("Public indexer not found");
248                 }
249                 else
250                     throw new ArgumentException("Invalid expression type");
251             }
252
253             while (member != null)
254             {
255                 var property = (PropertyInfo)member.Member;
256                 if (builder.Length != 0)
257                 {
258                     if (!indexed)
259                         builder.Insert(0, ".");
260                     else
261                         indexed = false;
262                 }
263
264                 builder.Insert(0, property.Name);
265
266                 //                              member = member.Expression as MemberExpression ?? (member.Expression as UnaryExpression)?.Operand as MemberExpression;
267                 member = member.Expression as MemberExpression ?? (member.Expression is UnaryExpression ? (member.Expression as UnaryExpression).Operand as MemberExpression : null);
268             }
269
270             return builder.ToString();
271         }
272     }
273 }