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