[Xaml] Support import other xaml as the source of resource dictionary
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / public / XamlBuild / CompiledConverters / RDSourceTypeConverter.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19
20 using Mono.Cecil;
21 using Mono.Cecil.Cil;
22
23 using static Mono.Cecil.Cil.Instruction;
24 using static Mono.Cecil.Cil.OpCodes;
25
26 using Tizen.NUI.Xaml.Build.Tasks;
27 using Tizen.NUI.Xaml;
28 using Tizen.NUI.Binding;
29 using System.Linq;
30
31 namespace Tizen.NUI.Xaml.Core.XamlC
32 {
33     class RDSourceTypeConverter : ICompiledTypeConverter
34     {
35         public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node)
36         {
37             var module = context.Module;
38
39             EmbeddedResource matchedResource = null;
40
41             foreach (var resource in module.Resources.OfType<EmbeddedResource>())
42             {
43                 if (resource.Name.StartsWith(context.EmbeddedResourceNameSpace) && resource.Name.EndsWith(value))
44                 {
45                     matchedResource = resource;
46                     break;
47                 }
48             }
49
50             if (null == matchedResource)
51             {
52                 foreach (var resource in module.Resources.OfType<EmbeddedResource>())
53                 {
54                     if (resource.Name.EndsWith(value))
55                     {
56                         matchedResource = resource;
57                         break;
58                     }
59                 }
60             }
61
62             if (null != matchedResource)
63             {
64                 string classname;
65                 if (matchedResource.IsResourceDictionaryXaml(module, out classname))
66                 {
67                     int lastIndex = classname.LastIndexOf('.');
68                     var realClassName = classname.Substring(lastIndex + 1);
69                     var typeref = XmlTypeExtensions.GetTypeReference(realClassName, module, node, XmlTypeExtensions.ModeOfGetType.Both);
70
71                     var typeName = matchedResource.Name.Replace('.', '_');
72                     var typeDefOfGetResource = module.Types.FirstOrDefault(type => type.FullName == "GetResource." + typeName);
73                     if (null != typeDefOfGetResource)
74                     {
75                         module.Types.Remove(typeDefOfGetResource);
76                         typeDefOfGetResource = null;
77                     }
78
79                     if (null == typeDefOfGetResource)
80                     {
81                         typeDefOfGetResource = new TypeDefinition("GetResource", typeName, TypeAttributes.NotPublic);
82                         typeDefOfGetResource.BaseType = typeref;
83                         module.Types.Add(typeDefOfGetResource);
84
85                         typeDefOfGetResource.AddDefaultConstructor(typeref);
86                     }
87
88                     var methodName = "GetResource";
89                     var methodOfGetResource = typeDefOfGetResource.Methods.FirstOrDefault(m => m.Name == methodName);
90
91                     if (null == methodOfGetResource)
92                     {
93                         methodOfGetResource = new MethodDefinition(methodName, MethodAttributes.Public, typeref);
94                         typeDefOfGetResource.Methods.Add(methodOfGetResource);
95                     }
96
97                     var constructor = typeDefOfGetResource.Methods.FirstOrDefault(m => m.IsConstructor);
98
99                     if (null != constructor)
100                     {
101                         constructor.Body.Instructions.Insert(constructor.Body.Instructions.Count - 1, Instruction.Create(OpCodes.Ldarg_0));
102                         constructor.Body.Instructions.Insert(constructor.Body.Instructions.Count - 1, Instruction.Create(OpCodes.Call, methodOfGetResource));
103                         constructor.Body.Instructions.Insert(constructor.Body.Instructions.Count - 1, Instruction.Create(OpCodes.Pop));
104                     }
105
106                     var rootnode = XamlTask.ParseXaml(matchedResource.GetResourceStream(), typeref);
107
108                     Exception exception;
109                     TryCoreCompile(methodOfGetResource, rootnode, context.EmbeddedResourceNameSpace, out exception);
110
111                     yield return Create(Newobj, constructor);
112                 }
113             }
114         }
115
116         internal static string GetPathForType(ModuleDefinition module, TypeReference type)
117         {
118             foreach (var ca in type.Module.GetCustomAttributes())
119             {
120                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((XamlTask.xamlAssemblyName, XamlTask.xamlNameSpace, "XamlResourceIdAttribute"))))
121                     continue;
122                 if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
123                     continue;
124                 return ca.ConstructorArguments[1].Value as string;
125             }
126             return null;
127         }
128
129         private bool TryCoreCompile(MethodDefinition initComp, ILRootNode rootnode, string resourceName, out Exception exception)
130         {
131             try
132             {
133                 var body = new MethodBody(initComp);
134                 var module = body.Method.Module;
135                 var type = initComp.DeclaringType;
136
137                 body.InitLocals = true;
138                 var il = body.GetILProcessor();
139                 il.Emit(OpCodes.Ldarg_0);
140                 var resourcePath = GetPathForType(module, type);
141
142                 il.Emit(Nop);
143
144                 List<Instruction> insOfAddEvent = new List<Instruction>();
145
146                 var visitorContext = new ILContext(il, body, insOfAddEvent, module, resourceName);
147
148                 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null);
149                 rootnode.Accept(new Tizen.NUI.Xaml.Build.Tasks.ExpandMarkupsVisitor(visitorContext), null);
150                 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
151                 rootnode.Accept(new CreateObjectVisitor(visitorContext), null);
152
153                 rootnode.Accept(new SetNamescopesAndRegisterNamesVisitor(visitorContext), null);
154                 rootnode.Accept(new SetFieldVisitor(visitorContext), null);
155                 rootnode.Accept(new SetResourcesVisitor(visitorContext), null);
156                 rootnode.Accept(new SetPropertiesVisitor(visitorContext, true), null);
157
158                 il.Emit(Ret);
159                 initComp.Body = body;
160                 exception = null;
161                 return true;
162             }
163             catch (Exception e)
164             {
165                 XamlParseException xamlParseException = e as XamlParseException;
166                 if (null != xamlParseException)
167                 {
168                     XamlParseException ret = new XamlParseException(xamlParseException.Message, xamlParseException.XmlInfo, xamlParseException.InnerException);
169                     exception = ret;
170                 }
171                 else
172                 {
173                     exception = e;
174                 }
175
176                 return false;
177             }
178         }
179     }
180 }
181