[NUI] Add Tizen.NUI.XamlBuild module
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / internal / Xaml / MarkupExtensions / StaticResourceExtension.cs
1 using System;
2 using System.Xml;
3 using System.Reflection;
4 using System.Linq;
5 using Tizen.NUI.Binding;
6 using Tizen.NUI.Binding;
7 using Tizen.NUI.EXaml;
8 using Tizen.NUI.EXaml.Build.Tasks;
9
10 namespace Tizen.NUI.Xaml
11 {
12     [ContentProperty("Key")]
13     internal sealed class StaticResourceExtension : IMarkupExtension
14     {
15         public string Key { get; set; }
16
17         public object ProvideValue(EXamlContext context)
18         {
19             object ret = null;
20             context.resourceDictionary.TryGetValue(Key, out ret);
21
22             if (null == ret)
23             {
24                 throw new Exception(String.Format("Key {0} can't be found in Resource", Key));
25             }
26
27             return ret;
28         }
29
30         public object ProvideValue(IServiceProvider serviceProvider)
31         {
32             if (serviceProvider == null)
33                 throw new ArgumentNullException(nameof(serviceProvider));
34             if (Key == null) {
35                 var lineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
36                 var lineInfo = (lineInfoProvider != null) ? lineInfoProvider.XmlLineInfo : new XmlLineInfo();
37                 throw new XamlParseException("you must specify a key in {StaticResource}", lineInfo);
38             }
39             var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideParentValues;
40             if (valueProvider == null)
41                 throw new ArgumentException();
42             var xmlLineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
43             var xmlLineInfo = xmlLineInfoProvider != null ? xmlLineInfoProvider.XmlLineInfo : null;
44             object resource = null;
45
46             foreach (var p in valueProvider.ParentObjects) {
47                 var irp = p as IResourcesProvider;
48                 var resDict = irp != null && irp.IsResourcesCreated ? irp.Resources : p as ResourceDictionary;
49                 if (resDict == null)
50                     continue;
51                 if (resDict.TryGetValue(Key, out resource))
52                     break;
53             }
54             resource = resource ?? GetApplicationLevelResource(Key, xmlLineInfo);
55
56             var bp = valueProvider.TargetProperty as BindableProperty;
57             var pi = valueProvider.TargetProperty as PropertyInfo;
58             var propertyType = bp?.ReturnType ?? pi?.PropertyType;
59             if (propertyType == null) {
60                 if (resource != null) {
61                     if (resource.GetType().GetTypeInfo().IsGenericType && (resource.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>) || resource.GetType().GetGenericTypeDefinition() == typeof(OnIdiom<>))) {
62                         // This is only there to support our backward compat story with pre 2.3.3 compiled Xaml project who was not providing TargetProperty
63                         var method = resource.GetType().GetRuntimeMethod("op_Implicit", new[] { resource.GetType() });
64                         if (method != null) {
65                             resource = method.Invoke(null, new[] { resource });
66                         }
67                     }
68                 }
69                 return resource;
70             }
71             if (propertyType.IsAssignableFrom(resource?.GetType()))
72                 return resource;
73             var implicit_op =  resource?.GetType().GetImplicitConversionOperator(fromType: resource?.GetType(), toType: propertyType)
74                             ?? propertyType.GetImplicitConversionOperator(fromType: resource?.GetType(), toType: propertyType);
75             if (implicit_op != null)
76                 return implicit_op.Invoke(resource, new [] { resource });
77
78             if (resource != null) { 
79                 //Special case for https://bugzilla.xamarin.com/show_bug.cgi?id=59818
80                 //On OnPlatform, check for an opImplicit from the targetType
81                 if (   Device.Flags != null
82                     && Device.Flags.Contains("xamlDoubleImplicitOpHack")
83                     && resource.GetType().GetTypeInfo().IsGenericType
84                     && (resource.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>))) {
85                     var tType = resource.GetType().GenericTypeArguments[0];
86                     var opImplicit = tType.GetImplicitConversionOperator(fromType: tType, toType: propertyType)
87                                     ?? propertyType.GetImplicitConversionOperator(fromType: tType, toType: propertyType);
88
89                     if (opImplicit != null) {
90                         //convert the OnPlatform<T> to T
91                         var opPlatformImplicitConversionOperator = resource?.GetType().GetImplicitConversionOperator(fromType: resource?.GetType(), toType: tType);
92                         resource = opPlatformImplicitConversionOperator?.Invoke(null, new[] { resource });
93
94                         //and convert to toType
95                         resource = opImplicit.Invoke(null, new[] { resource });
96                         return resource;
97                     }
98                 }
99             }
100             return resource;
101         }
102
103         internal object GetApplicationLevelResource(string key, IXmlLineInfo xmlLineInfo)
104         {
105             object resource = null;
106             if (Application.Current == null || !((IResourcesProvider)Application.Current).IsResourcesCreated || !Application.Current.Resources.TryGetValue(Key, out resource))
107                 throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
108             return resource;
109         }
110     }
111 }