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