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