Follow formatting NUI
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Xaml / ApplyPropertiesVisitor.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Reflection;
6 using System.Xml;
7 using Tizen.NUI.Binding.Internals;
8 using Tizen.NUI.Binding;
9 using Tizen.NUI.StyleSheets;
10
11 using static System.String;
12
13 namespace Tizen.NUI.Xaml
14 {
15     internal class ApplyPropertiesVisitor : IXamlNodeVisitor
16     {
17         public static readonly IList<XmlName> Skips = new List<XmlName> {
18             XmlName.xKey,
19             XmlName.xTypeArguments,
20             XmlName.xArguments,
21             XmlName.xFactoryMethod,
22             XmlName.xName,
23             XmlName.xDataType
24         };
25
26         public ApplyPropertiesVisitor(HydrationContext context, bool stopOnResourceDictionary = false)
27         {
28             Context = context;
29             StopOnResourceDictionary = stopOnResourceDictionary;
30         }
31
32         Dictionary<INode, object> Values => Context.Values;
33         HydrationContext Context { get; }
34
35         public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
36         public bool StopOnDataTemplate => true;
37         public bool StopOnResourceDictionary { get; }
38         public bool VisitNodeOnDataTemplate => true;
39         public bool SkipChildren(INode node, INode parentNode) => false;
40         public bool IsResourceDictionary(ElementNode node) => typeof(ResourceDictionary).IsAssignableFrom(Context.Types[node]);
41
42         public void Visit(ValueNode node, INode parentNode)
43         {
44             var parentElement = parentNode as IElementNode;
45             var value = Values[node];
46             var source = Values[parentNode];
47             XmlName propertyName;
48
49             if (TryGetPropertyName(node, parentNode, out propertyName))
50             {
51                 if (TrySetRuntimeName(propertyName, source, value, node))
52                     return;
53                 if (Skips.Contains(propertyName))
54                     return;
55                 if (parentElement.SkipProperties.Contains(propertyName))
56                     return;
57                 if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
58                     return;
59                 SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
60             }
61             else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
62             {
63                 // Collection element, implicit content, or implicit collection element.
64                 var contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo());
65                 if (contentProperty != null)
66                 {
67                     var name = new XmlName(((ElementNode)parentNode).NamespaceURI, contentProperty);
68                     if (Skips.Contains(name))
69                         return;
70                     if (parentElement.SkipProperties.Contains(propertyName))
71                         return;
72                     SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
73                 }
74             }
75         }
76
77         public void Visit(MarkupNode node, INode parentNode)
78         {
79         }
80
81         public void Visit(ElementNode node, INode parentNode)
82         {
83             XmlName propertyName;
84             if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent)
85             {
86                 var s0 = Values[parentNode];
87                 if (s0 is ElementTemplate)
88                 {
89                     SetTemplate(s0 as ElementTemplate, node);
90                     return;
91                 }
92             }
93
94             var parentElement = parentNode as IElementNode;
95             propertyName = XmlName.Empty;
96
97             //Simplify ListNodes with single elements
98             var pList = parentNode as ListNode;
99             if (pList != null && pList.CollectionItems.Count == 1)
100             {
101                 propertyName = pList.XmlName;
102                 parentNode = parentNode.Parent;
103                 parentElement = parentNode as IElementNode;
104             }
105
106             var value = Values[node];
107
108             if (propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName))
109             {
110                 if (Skips.Contains(propertyName))
111                     return;
112                 if (parentElement == null)
113                     return;
114                 if (parentElement.SkipProperties.Contains(propertyName))
115                     return;
116
117                 var source = Values[parentNode];
118                 ProvideValue(ref value, node, source, propertyName);
119                 SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
120             }
121             else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
122             {
123                 var source = Values[parentNode];
124                 ProvideValue(ref value, node, source, XmlName.Empty);
125                 string contentProperty;
126                 Exception xpe = null;
127                 var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
128
129                 //ResourceDictionary
130                 if (xpe == null && TryAddToResourceDictionary(source as ResourceDictionary, value, xKey, node, out xpe))
131                     return;
132
133                 // Dictionary with string key
134                 if (xpe == null && xKey != null)
135                 {
136                     var indexer = GetIndexer(source, typeof(string), value.GetType());
137                     if (indexer != null)
138                     {
139                         indexer.SetValue(source, value, new[] { xKey });
140                         return;
141                     }
142                 }
143
144                 // Collection element, implicit content, or implicit collection element.
145                 if (xpe == null && typeof(IEnumerable).IsAssignableFrom(Context.Types[parentElement]) && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1))
146                 {
147                     var addMethod =
148                         Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
149
150                     addMethod?.Invoke(source, new[] { value });
151                     return;
152                 }
153                 if (xpe == null && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1))
154                 {
155                     //if there are similar parameters in the function, this will exist issue.
156                     var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
157                     if (addMethod != null) addMethod.Invoke(source, new[] { value });
158                     return;
159                 }
160                 if (xpe == null && (contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null)
161                 {
162                     var name = new XmlName(node.NamespaceURI, contentProperty);
163                     if (Skips.Contains(name))
164                         return;
165                     if (parentElement.SkipProperties.Contains(propertyName))
166                         return;
167
168                     SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
169                     return;
170                 }
171
172                 xpe = xpe ?? new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
173                 if (Context.ExceptionHandler != null)
174                     Context.ExceptionHandler(xpe);
175                 throw xpe;
176             }
177             else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
178             {
179                 var source = Values[parentNode.Parent];
180                 ProvideValue(ref value, node, source, XmlName.Empty);
181                 var parentList = (ListNode)parentNode;
182                 if (Skips.Contains(parentList.XmlName))
183                     return;
184                 Exception xpe = null;
185                 var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
186
187                 object _;
188                 var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _) as IEnumerable;
189                 if (collection == null)
190                     xpe = new XamlParseException($"Property {parentList.XmlName.LocalName} is null or is not IEnumerable", node);
191
192                 if (xpe == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, node, out xpe))
193                     return;
194
195                 MethodInfo addMethod;
196                 if (xpe == null && (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) != null)
197                 {
198                     addMethod.Invoke(collection, new[] { Values[node] });
199                     return;
200                 }
201                 xpe = xpe ?? new XamlParseException($"Value of {parentList.XmlName.LocalName} does not have a Add() method", node);
202                 if (Context.ExceptionHandler != null)
203                     Context.ExceptionHandler(xpe);
204                 else
205                     throw xpe;
206             }
207         }
208
209
210
211         public void Visit(RootNode node, INode parentNode)
212         {
213         }
214
215         public void Visit(ListNode node, INode parentNode)
216         {
217         }
218
219         public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
220         {
221             name = default(XmlName);
222             var parentElement = parentNode as IElementNode;
223             if (parentElement == null)
224                 return false;
225             foreach (var kvp in parentElement.Properties)
226             {
227                 if (kvp.Value != node)
228                     continue;
229                 name = kvp.Key;
230                 return true;
231             }
232             return false;
233         }
234
235         internal static bool IsCollectionItem(INode node, INode parentNode)
236         {
237             var parentList = parentNode as IListNode;
238             if (parentList == null)
239                 return false;
240             return parentList.CollectionItems.Contains(node);
241         }
242
243         internal static string GetContentPropertyName(System.Reflection.TypeInfo typeInfo)
244         {
245             while (typeInfo != null)
246             {
247                 var propName = GetContentPropertyName(typeInfo.CustomAttributes);
248                 if (propName != null)
249                     return propName;
250                 typeInfo = typeInfo?.BaseType?.GetTypeInfo();
251             }
252             return null;
253         }
254
255         void ProvideValue(ref object value, ElementNode node, object source, XmlName propertyName)
256         {
257             var markupExtension = value as IMarkupExtension;
258             var valueProvider = value as IValueProvider;
259
260             if (markupExtension == null && valueProvider == null)
261                 return;
262
263             XamlServiceProvider serviceProvider = null;
264             if (value.GetType().GetTypeInfo().GetCustomAttribute<AcceptEmptyServiceProviderAttribute>() == null)
265                 serviceProvider = new XamlServiceProvider(node, Context);
266
267             if (serviceProvider != null && serviceProvider.IProvideValueTarget is XamlValueTargetProvider && propertyName != XmlName.Empty)
268             {
269                 (serviceProvider.IProvideValueTarget as XamlValueTargetProvider).TargetProperty = GetTargetProperty(source, propertyName, Context, node);
270             }
271
272             if (markupExtension != null)
273                 value = markupExtension.ProvideValue(serviceProvider);
274             else if (valueProvider != null)
275                 value = valueProvider.ProvideValue(serviceProvider);
276         }
277
278         static string GetContentPropertyName(IEnumerable<CustomAttributeData> attributes)
279         {
280             var contentAttribute =
281                 attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
282             if (contentAttribute == null || contentAttribute.ConstructorArguments.Count != 1)
283                 return null;
284             if (contentAttribute.ConstructorArguments[0].ArgumentType == typeof(string))
285                 return (string)contentAttribute.ConstructorArguments[0].Value;
286             return null;
287         }
288
289         static bool GetRealNameAndType(ref Type elementType, string namespaceURI, ref string localname,
290             HydrationContext context, IXmlLineInfo lineInfo)
291         {
292             var dotIdx = localname.IndexOf('.');
293             if (dotIdx > 0)
294             {
295                 var typename = localname.Substring(0, dotIdx);
296                 localname = localname.Substring(dotIdx + 1);
297                 XamlParseException xpe;
298                 elementType = XamlParser.GetElementType(new XmlType(namespaceURI, typename, null), lineInfo,
299                     context.RootElement.GetType().GetTypeInfo().Assembly, out xpe);
300
301                 if (xpe != null)
302                     throw xpe;
303                 return true;
304             }
305             return false;
306         }
307
308         static BindableProperty GetBindableProperty(Type elementType, string localName, IXmlLineInfo lineInfo,
309             bool throwOnError = false)
310         {
311 #if NETSTANDARD1_0
312             var bindableFieldInfo = elementType.GetFields().FirstOrDefault(fi => fi.Name == localName + "Property");
313 #else
314             var bindableFieldInfo = elementType.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).FirstOrDefault(fi => fi.Name == localName + "Property");
315
316             if (null == bindableFieldInfo)
317             {
318                 bindableFieldInfo = elementType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy).FirstOrDefault(fi => fi.Name == localName + "Property");
319             }
320 #endif
321             Exception exception = null;
322             if (exception == null && bindableFieldInfo == null)
323             {
324                 exception =
325                     new XamlParseException(
326                         Format("BindableProperty {0} not found on {1}", localName + "Property", elementType.Name), lineInfo);
327             }
328
329             if (exception == null)
330                 return bindableFieldInfo.GetValue(null) as BindableProperty;
331             if (throwOnError)
332                 throw exception;
333             return null;
334         }
335
336         static object GetTargetProperty(object xamlelement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo)
337         {
338             var localName = propertyName.LocalName;
339             //If it's an attached BP, update elementType and propertyName
340             var bpOwnerType = xamlelement.GetType();
341             GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
342             var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
343
344             if (property != null)
345                 return property;
346
347             var elementType = xamlelement.GetType();
348             var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
349             return propertyInfo;
350         }
351
352         public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydrationContext context, IXmlLineInfo lineInfo)
353         {
354             var localName = propertyName.LocalName;
355             var serviceProvider = new XamlServiceProvider(node, context);
356             Exception xpe = null;
357             var xKey = node is IElementNode && ((IElementNode)node).Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)((IElementNode)node).Properties[XmlName.xKey]).Value as string : null;
358
359             //If it's an attached BP, update elementType and propertyName
360             var bpOwnerType = xamlelement.GetType();
361             var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
362
363             var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
364
365             //If the target is an event, connect
366             if (xpe == null && TryConnectEvent(xamlelement, localName, attached, value, rootElement, lineInfo, out xpe))
367                 return;
368
369             //If Value is DynamicResource and it's a BP, SetDynamicResource
370             if (xpe == null && TrySetDynamicResource(xamlelement, property, value, lineInfo, out xpe))
371                 return;
372
373             //If value is BindingBase, SetBinding
374             if (xpe == null && TrySetBinding(xamlelement, property, localName, value, lineInfo, out xpe))
375                 return;
376
377             //Call TrySetProperty first and then TrySetValue to keep the code logic consistent whether it is through xaml or code.
378             //If we can assign that value to a normal property, let's do it
379             if (xpe == null && TrySetProperty(xamlelement, localName, value, lineInfo, serviceProvider, context, out xpe))
380                 return;
381
382             //If it's a BindableProberty, SetValue
383             if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe))
384                 return;
385
386             //If it's an already initialized property, add to it
387             if (xpe == null && TryAddToProperty(xamlelement, propertyName, value, xKey, lineInfo, serviceProvider, context, out xpe))
388                 return;
389
390             xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exist, or is not assignable, or mismatching type between value and property", lineInfo);
391             if (context.ExceptionHandler != null)
392                 context.ExceptionHandler(xpe);
393             else
394                 throw xpe;
395         }
396
397         public static object GetPropertyValue(object xamlElement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo, out object targetProperty)
398         {
399             var localName = propertyName.LocalName;
400             Exception xpe = null;
401             object value;
402             targetProperty = null;
403
404             //If it's an attached BP, update elementType and propertyName
405             var bpOwnerType = xamlElement.GetType();
406             var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
407             var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
408
409             //If it's a BindableProberty, GetValue
410             if (xpe == null && TryGetValue(xamlElement, property, attached, out value, lineInfo, out xpe, out targetProperty))
411                 return value;
412
413             //If it's a normal property, get it
414             if (xpe == null && TryGetProperty(xamlElement, localName, out value, lineInfo, context, out xpe, out targetProperty))
415                 return value;
416
417             xpe = xpe ?? new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
418             if (context.ExceptionHandler != null)
419                 context.ExceptionHandler(xpe);
420             else
421                 throw xpe;
422
423             return null;
424         }
425
426         static bool TryConnectEvent(object element, string localName, bool attached, object value, object rootElement, IXmlLineInfo lineInfo, out Exception exception)
427         {
428             exception = null;
429
430             if (attached)
431                 return false;
432
433             var elementType = element.GetType();
434             var eventInfo = elementType.GetRuntimeEvent(localName);
435             var stringValue = value as string;
436
437             if (eventInfo == null || IsNullOrEmpty(stringValue))
438                 return false;
439
440             var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value);
441             if (methodInfo == null)
442             {
443                 exception = new XamlParseException($"No method {value} found on type {rootElement.GetType()}", lineInfo);
444                 return false;
445             }
446
447             try
448             {
449                 eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
450                 return true;
451             }
452             catch (ArgumentException ae)
453             {
454                 exception = new XamlParseException($"Method {stringValue} does not have the correct signature", lineInfo, ae);
455             }
456             return false;
457         }
458
459         static bool TrySetDynamicResource(object element, BindableProperty property, object value, IXmlLineInfo lineInfo, out Exception exception)
460         {
461             exception = null;
462
463             var elementType = element.GetType();
464             var dynamicResource = value as DynamicResource;
465             var bindable = element as BindableObject;
466
467             if (dynamicResource == null || property == null)
468                 return false;
469
470             if (bindable == null)
471             {
472                 exception = new XamlParseException($"{elementType.Name} is not a BindableObject", lineInfo);
473                 return false;
474             }
475
476             bindable.SetDynamicResource(property, dynamicResource.Key);
477             return true;
478         }
479
480         static bool TrySetBinding(object element, BindableProperty property, string localName, object value, IXmlLineInfo lineInfo, out Exception exception)
481         {
482             exception = null;
483
484             var elementType = element.GetType();
485             var binding = value.ConvertTo(typeof(BindingBase), pinfoRetriever: null, serviceProvider: null) as BindingBase;
486             var bindable = element as BindableObject;
487
488             if (binding == null)
489                 return false;
490
491             if (bindable != null && property != null)
492             {
493                 bindable.SetBinding(property, binding);
494                 return true;
495             }
496
497             if (property != null)
498                 exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support native bindings", lineInfo);
499
500             return false;
501         }
502
503         static bool TrySetValue(object element, BindableProperty property, bool attached, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
504         {
505             exception = null;
506
507             var elementType = element.GetType();
508             var bindable = element as BindableObject;
509
510             if (property == null)
511                 return false;
512
513             if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
514                 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = property;
515
516             Func<MemberInfo> minforetriever;
517             if (attached)
518                 minforetriever = () => property.DeclaringType.GetRuntimeMethod("Get" + property.PropertyName, new[] { typeof(BindableObject) });
519             else
520             {
521                 minforetriever = () => property.DeclaringType.GetRuntimeProperties().LastOrDefault(p => p.Name == property.PropertyName);
522             }
523             //minforetriever = () => property.DeclaringType.GetRuntimeProperty(property.PropertyName);
524             var convertedValue = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider);
525
526             if (bindable != null)
527             {
528                 //SetValue doesn't throw on mismatching type, so check before to get a chance to try the property setting or the collection adding
529                 var nullable = property.ReturnTypeInfo.IsGenericType &&
530                                property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
531                 if ((convertedValue == null && (!property.ReturnTypeInfo.IsValueType || nullable)) ||
532                     (property.ReturnType.IsInstanceOfType(convertedValue)))
533                 {
534                     bindable.SetValue(property, convertedValue);
535                     return true;
536                 }
537
538                 // This might be a collection; see if we can add to it
539                 return TryAddValue(bindable, property, value, serviceProvider);
540             }
541
542             exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support setting native BindableProperties", lineInfo);
543             return false;
544         }
545
546         static bool TryGetValue(object element, BindableProperty property, bool attached, out object value, IXmlLineInfo lineInfo, out Exception exception, out object targetProperty)
547         {
548             exception = null;
549             value = null;
550             targetProperty = property;
551             var elementType = element.GetType();
552             var bindable = element as BindableObject;
553
554             if (property == null)
555                 return false;
556
557             if (bindable == null)
558                 return false;
559
560             value = bindable.GetValue(property);
561             return true;
562         }
563
564         static bool TrySetProperty(object element, string localName, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
565         {
566             exception = null;
567
568             var elementType = element.GetType();
569             var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
570             MethodInfo setter;
571             if (propertyInfo == null || !propertyInfo.CanWrite || (setter = propertyInfo.SetMethod) == null)
572                 return false;
573
574             if (!IsVisibleFrom(setter, context.RootElement))
575                 return false;
576
577             if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
578                 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = propertyInfo;
579
580             object convertedValue = GetConvertedValue(propertyInfo.PropertyType, value, () => propertyInfo, serviceProvider);
581
582             if (null == convertedValue)
583             {
584                 var methods = propertyInfo.PropertyType.GetMethods().Where(a => a.Name == "op_Implicit");
585
586                 foreach (var method in methods)
587                 {
588                     var paramType = method.GetParameters()[0].ParameterType;
589                     convertedValue = GetConvertedValue(paramType, value, () => propertyInfo, serviceProvider);
590
591                     if (null != convertedValue)
592                     {
593                         var realValue = Activator.CreateInstance(propertyInfo.PropertyType);
594                         convertedValue = method.Invoke(realValue, new object[] { convertedValue });
595
596                         if (null != convertedValue)
597                         {
598                             break;
599                         }
600                     }
601                 }
602             }
603
604             if (null == convertedValue)
605             {
606                 return false;
607             }
608
609             setter.Invoke(element, new object[] { convertedValue });
610             return true;
611         }
612
613         static private object GetConvertedValue(Type valueType, object value, Func<MemberInfo> minfoRetriever, XamlServiceProvider serviceProvider)
614         {
615             try
616             {
617                 object convertedValue = value.ConvertTo(valueType, minfoRetriever, serviceProvider);
618
619                 if (convertedValue != null && !valueType.IsInstanceOfType(convertedValue))
620                 {
621                     return null;
622                 }
623
624                 return convertedValue;
625             }
626             catch
627             {
628                 return null;
629             }
630         }
631
632         static bool TryGetProperty(object element, string localName, out object value, IXmlLineInfo lineInfo, HydrationContext context, out Exception exception, out object targetProperty)
633         {
634             exception = null;
635             value = null;
636             var elementType = element.GetType();
637             PropertyInfo propertyInfo = null;
638             try
639             {
640                 propertyInfo = elementType.GetRuntimeProperty(localName);
641             }
642             catch (AmbiguousMatchException)
643             {
644                 // Get most derived instance of property
645                 foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localName))
646                 {
647                     if (propertyInfo == null || propertyInfo.DeclaringType.IsAssignableFrom(property.DeclaringType))
648                         propertyInfo = property;
649                 }
650             }
651             MethodInfo getter;
652             targetProperty = propertyInfo;
653             if (propertyInfo == null || !propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null)
654                 return false;
655
656             if (!IsVisibleFrom(getter, context.RootElement))
657                 return false;
658
659             value = getter.Invoke(element, new object[] { });
660             return true;
661         }
662
663         static bool IsVisibleFrom(MethodInfo method, object rootElement)
664         {
665             if (method.IsPublic)
666                 return true;
667             if (method.IsPrivate && method.DeclaringType == rootElement.GetType())
668                 return true;
669             if ((method.IsAssembly || method.IsFamilyOrAssembly) && method.DeclaringType.AssemblyQualifiedName == rootElement.GetType().AssemblyQualifiedName)
670                 return true;
671             if (method.IsFamily && method.DeclaringType.IsAssignableFrom(rootElement.GetType()))
672                 return true;
673             return false;
674         }
675
676         static bool TryAddToProperty(object element, XmlName propertyName, object value, string xKey, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
677         {
678             exception = null;
679
680             object targetProperty;
681             var collection = GetPropertyValue(element, propertyName, context, lineInfo, out targetProperty) as IEnumerable;
682
683             if (collection == null)
684                 return false;
685
686             if (exception == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, lineInfo, out exception))
687                 return true;
688
689             if (exception != null)
690                 return false;
691
692             var addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
693             if (addMethod == null)
694                 return false;
695
696             if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
697                 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = targetProperty;
698
699             addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
700             return true;
701         }
702
703         static bool TryAddToResourceDictionary(ResourceDictionary resourceDictionary, object value, string xKey, IXmlLineInfo lineInfo, out Exception exception)
704         {
705             exception = null;
706
707             if (resourceDictionary == null)
708                 return false;
709
710             if (xKey != null)
711                 resourceDictionary.Add(xKey, value);
712             else if (value is Tizen.NUI.Binding.Style)
713                 resourceDictionary.Add((Tizen.NUI.Binding.Style)value);
714             else if (value is ResourceDictionary)
715                 resourceDictionary.Add((ResourceDictionary)value);
716             else if (value is StyleSheets.StyleSheet)
717                 resourceDictionary.Add((StyleSheets.StyleSheet)value);
718             else
719             {
720                 exception = new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
721                 return false;
722             }
723             return true;
724         }
725
726         void SetTemplate(ElementTemplate dt, INode node)
727         {
728 #pragma warning disable 0612
729             ((IDataTemplate)dt).LoadTemplate = () =>
730             {
731 #pragma warning restore 0612
732                 var cnode = node.Clone();
733                 var context = new HydrationContext { ParentContext = Context, RootElement = Context.RootElement };
734                 cnode.Accept(new XamlNodeVisitor((n, parent) => n.Parent = parent), node.Parent); //set parents for {StaticResource}
735                 cnode.Accept(new ExpandMarkupsVisitor(context), null);
736                 cnode.Accept(new NamescopingVisitor(context), null);
737                 cnode.Accept(new CreateValuesVisitor(context), null);
738                 cnode.Accept(new RegisterXNamesVisitor(context), null);
739                 cnode.Accept(new FillResourceDictionariesVisitor(context), null);
740                 cnode.Accept(new ApplyPropertiesVisitor(context, true), null);
741                 return context.Values[cnode];
742             };
743         }
744
745         static bool TryAddValue(BindableObject bindable, BindableProperty property, object value, XamlServiceProvider serviceProvider)
746         {
747             if (property?.ReturnTypeInfo?.GenericTypeArguments == null)
748             {
749                 return false;
750             }
751
752             if (property.ReturnType == null)
753             {
754                 return false;
755             }
756
757             if (property.ReturnTypeInfo.GenericTypeArguments.Length != 1 ||
758                 !property.ReturnTypeInfo.GenericTypeArguments[0].IsInstanceOfType(value))
759                 return false;
760
761             // This might be a collection we can add to; see if we can find an Add method
762             var addMethod = GetAllRuntimeMethods(property.ReturnType)
763                 .FirstOrDefault(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
764             if (addMethod == null)
765                 return false;
766
767             // If there's an add method, get the collection
768             var collection = bindable.GetValue(property);
769
770             // And add the new value to it
771             addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
772             return true;
773         }
774
775         static IEnumerable<MethodInfo> GetAllRuntimeMethods(Type type)
776         {
777             return type.GetRuntimeMethods()
778                 .Concat(type.GetTypeInfo().ImplementedInterfaces.SelectMany(t => t.GetRuntimeMethods()));
779         }
780
781         bool TrySetRuntimeName(XmlName propertyName, object source, object value, ValueNode node)
782         {
783             if (propertyName != XmlName.xName)
784                 return false;
785
786             var runTimeName = source.GetType().GetTypeInfo().GetCustomAttribute<RuntimeNamePropertyAttribute>();
787             if (runTimeName == null)
788                 return false;
789
790             SetPropertyValue(source, new XmlName("", runTimeName.Name), value, Context.RootElement, node, Context, node);
791             return true;
792         }
793
794         private PropertyInfo GetIndexer(object source, Type keyType, Type valueType) => source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(p => p.Name == "Item" && p.PropertyType.IsAssignableFrom(valueType) && p.GetIndexParameters().Length != 0 && p.GetIndexParameters()[0].ParameterType == keyType);
795     }
796 }