[Xaml] Support import other xaml as the source of resource dictionary
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / public / XamlBuild / XamlTask.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 using System.Diagnostics;
20 using System.IO;
21 using System.Xml;
22
23 using Microsoft.Build.Framework;
24 using Microsoft.Build.Utilities;
25
26 using Mono.Cecil;
27
28 namespace Tizen.NUI.Xaml.Build.Tasks
29 {
30     [LoadInSeparateAppDomain]
31     public abstract class XamlTask : MarshalByRefObject, ITask
32     {
33         internal const string nuiAssemblyName = "Tizen.NUI";
34         internal const string nuiNameSpace = "Tizen.NUI";
35
36         internal const string xamlAssemblyName = "Tizen.NUI";
37         internal const string xamlNameSpace = "Tizen.NUI.Xaml";
38
39         internal const string bindingAssemblyName = "Tizen.NUI";
40         internal const string bindingNameSpace = "Tizen.NUI.Binding";
41         internal const string bindingInternalNameSpace = "Tizen.NUI.Binding.Internals";
42
43         [Required]
44         public string Assembly { get; set; }
45         public string DependencyPaths { get; set; }
46         public string ReferencePath { get; set; }
47         [Obsolete("this is no longer used")]
48         public int Verbosity { get; set; }
49         public bool DebugSymbols { get; set; }
50         public string DebugType { get; set; }
51         public string XamlFilePath { get; set; }
52         public bool NeedDebug { get; set; } = false;
53
54         protected TaskLoggingHelper LoggingHelper { get; }
55
56         internal XamlTask()
57         {
58             LoggingHelper = new TaskLoggingHelper(this);
59         }
60
61         public IBuildEngine BuildEngine { get; set; }
62         public ITaskHost HostObject { get; set; }
63
64         public bool Execute()
65         {
66             IList<Exception> _;
67             return Execute(out _);
68         }
69
70         public abstract bool Execute(out IList<Exception> thrownExceptions);
71
72         internal static ILRootNode ParseXaml(Stream stream, TypeReference typeReference)
73         {
74             ILRootNode rootnode = null;
75             using (var reader = XmlReader.Create(stream)) {
76                 while (reader.Read()) {
77                     //Skip until element
78                     if (reader.NodeType == XmlNodeType.Whitespace)
79                         continue;
80                     if (reader.NodeType != XmlNodeType.Element) {
81                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
82                         continue;
83                     }
84
85                     XamlParser.ParseXaml(
86                         rootnode = new ILRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), typeReference, reader as IXmlNamespaceResolver), reader);
87                     break;
88                 }
89             }
90             return rootnode;
91         }
92
93         internal static string GetResourceIdForPath(ModuleDefinition module, string path)
94         {
95             foreach (var ca in module.GetCustomAttributes())
96             {
97                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
98                     continue;
99                 if (ca.ConstructorArguments[1].Value as string != path)
100                     continue;
101                 return ca.ConstructorArguments[0].Value as string;
102             }
103             return null;
104         }
105
106         internal static string GetPathForType(ModuleDefinition module, TypeReference type)
107         {
108             foreach (var ca in type.Module.GetCustomAttributes())
109             {
110                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((xamlAssemblyName, xamlNameSpace, "XamlResourceIdAttribute"))))
111                     continue;
112                 if (!TypeRefComparer.Default.Equals(ca.ConstructorArguments[2].Value as TypeReference, type))
113                     continue;
114                 return ca.ConstructorArguments[1].Value as string;
115             }
116             return null;
117         }
118
119         internal static IList<XmlnsDefinitionAttribute> s_xmlnsDefinitions
120         {
121             get;
122         } = new List<XmlnsDefinitionAttribute>();
123     }
124
125     public static class CecilExtensions
126     {
127         public static bool IsResourceDictionaryXaml(this EmbeddedResource resource, ModuleDefinition module, out string classname)
128         {
129             classname = null;
130
131             if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture))
132                 return false;
133
134             using (var resourceStream = resource.GetResourceStream())
135             {
136                 var xmlDoc = new XmlDocument();
137                 xmlDoc.Load(resourceStream);
138
139                 var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
140
141                 var root = xmlDoc.SelectSingleNode("/*", nsmgr);
142                 if (root == null)
143                     return false;
144
145                 var rootClass = root.Attributes["Class", XamlParser.X2006Uri] ??
146                                 root.Attributes["Class", XamlParser.X2009Uri];
147                 if (rootClass != null)
148                 {
149                     classname = rootClass.Value;
150                     return true;
151                 }
152
153                 if ("ResourceDictionary" == root.Name)
154                 {
155                     classname = "Tizen.NUI.Binding.ResourceDictionary";
156                     return true;
157                 }
158
159                 return false;
160             }
161         }
162
163         public static bool IsXaml(this EmbeddedResource resource, ModuleDefinition module, out string classname)
164         {
165             classname = null;
166             if (!resource.Name.EndsWith(".xaml", StringComparison.InvariantCulture))
167                 return false;
168
169             using (var resourceStream = resource.GetResourceStream()) {
170                 var xmlDoc = new XmlDocument();
171                 xmlDoc.Load(resourceStream);
172
173                 var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
174
175                 var root = xmlDoc.SelectSingleNode("/*", nsmgr);
176                 if (root == null)
177                     return false;
178
179                 var rootClass = root.Attributes["Class", XamlParser.X2006Uri] ??
180                                 root.Attributes["Class", XamlParser.X2009Uri];
181                 if (rootClass != null) {
182                     classname = rootClass.Value;
183                     return true;
184                 }
185
186                 //no x:Class, but it might be a RD without x:Class and with <?xaml-comp compile="true" ?>
187                 //in that case, it has a XamlResourceIdAttribute
188                 var typeRef = GetTypeForResourceId(module, resource.Name);
189                 if (typeRef != null) {
190                     classname = typeRef.FullName;
191                     return true;
192                 }
193
194                 return false;
195             }
196         }
197
198         public static bool IsXaml(Stream resourceStream, ModuleDefinition module, out string classname)
199         {
200             classname = null;
201
202             var xmlDoc = new XmlDocument();
203             xmlDoc.Load(resourceStream);
204
205             var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
206
207             var root = xmlDoc.SelectSingleNode("/*", nsmgr);
208             if (root == null)
209                 return false;
210
211             var rootClass = root.Attributes["Class", XamlParser.X2006Uri] ??
212                             root.Attributes["Class", XamlParser.X2009Uri];
213             if (rootClass != null)
214             {
215                 classname = rootClass.Value;
216                 return true;
217             }
218
219             return false;
220         }
221
222         static TypeReference GetTypeForResourceId(ModuleDefinition module, string resourceId)
223         {
224             foreach (var ca in module.GetCustomAttributes()) {
225                 if (!TypeRefComparer.Default.Equals(ca.AttributeType, module.ImportReference((XamlTask.xamlAssemblyName, XamlTask.xamlNameSpace, "XamlResourceIdAttribute"))))
226                     continue;
227                 if (ca.ConstructorArguments[0].Value as string != resourceId)
228                     continue;
229                 return ca.ConstructorArguments[2].Value as TypeReference;
230             }
231             return null;
232         }
233     }
234 }
235