[NUI] Add file comment and end empty line
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / internal / Xaml / XamlLoader.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
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Diagnostics;
22 using System.IO;
23 using System.Reflection;
24 using System.Text.RegularExpressions;
25 using System.Xml;
26 using Tizen.NUI.Binding;
27 using Tizen.NUI.Binding.Internals;
28
29 namespace Tizen.NUI.Xaml.Internals
30 {
31     [Obsolete ("Replaced by ResourceLoader")]
32     internal static class XamlLoader
33     {
34         static Func<Type, string> xamlFileProvider;
35
36         public static Func<Type, string> XamlFileProvider {
37             get { return xamlFileProvider; }
38             internal set {
39                 xamlFileProvider = value;
40                 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
41                 //¯\_(ツ)_/¯ the previewer forgot to set that bool
42                 DoNotThrowOnExceptions = value != null;
43             }
44         }
45
46         internal static bool DoNotThrowOnExceptions { get; set; }
47     }
48 }
49
50 namespace Tizen.NUI.Xaml
51 {
52     static internal class XamlLoader
53     {
54         public static void Load(object view, string xaml)
55         {
56             using (var textReader = new StringReader(xaml))
57             using (var reader = XmlReader.Create(textReader))
58             {
59                 while (reader.Read())
60                 {
61                     //Skip until element
62                     if (reader.NodeType == XmlNodeType.Whitespace)
63                         continue;
64                     if (reader.NodeType == XmlNodeType.XmlDeclaration)
65                         continue;
66                     if (reader.NodeType != XmlNodeType.Element) {
67                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
68                         continue;
69                     }
70
71                     for (int i = 0; i < 100; i++)
72                     {
73                         XmlType temp = new XmlType(reader.NamespaceURI, reader.Name, null);
74                     }
75
76                     XmlType xmlType = new XmlType(reader.NamespaceURI, reader.Name, null);
77
78                     for (int i = 0; i < 100; i++)
79                     {
80                         new RuntimeRootNode(xmlType, view, (IXmlNamespaceResolver)reader);
81                     }
82
83                     var rootnode = new RuntimeRootNode (xmlType, view, (IXmlNamespaceResolver)reader);
84                     XamlParser.ParseXaml (rootnode, reader);
85                     Visit (rootnode, new HydrationContext {
86                         RootElement = view,
87 #pragma warning disable 0618
88                         ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
89 #pragma warning restore 0618
90                     });
91                     break;
92                 }
93             }
94         }
95
96         [Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
97         public static object Create (string xaml, bool doNotThrow = false)
98         {
99             object inflatedView = null;
100             using (var textreader = new StringReader(xaml))
101             using (var reader = XmlReader.Create (textreader)) {
102                 while (reader.Read ()) {
103                     //Skip until element
104                     if (reader.NodeType == XmlNodeType.Whitespace)
105                         continue;
106                     if (reader.NodeType == XmlNodeType.XmlDeclaration)
107                         continue;
108                     if (reader.NodeType != XmlNodeType.Element) {
109                         Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
110                         continue;
111                     }
112
113                     var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
114                     XamlParser.ParseXaml (rootnode, reader);
115                     var visitorContext = new HydrationContext {
116                         ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
117                     };
118                     var cvv = new CreateValuesVisitor (visitorContext);
119                     cvv.Visit ((ElementNode)rootnode, null);
120                     inflatedView = rootnode.Root = visitorContext.Values [rootnode];
121                     visitorContext.RootElement = inflatedView as BindableObject;
122
123                     Visit (rootnode, visitorContext);
124                     break;
125                 }
126             }
127             return inflatedView;
128         }
129
130         static void Visit (RootNode rootnode, HydrationContext visitorContext)
131         {
132             rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
133             rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null);
134             rootnode.Accept (new PruneIgnoredNodesVisitor(), null);
135             rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference}
136             rootnode.Accept (new CreateValuesVisitor (visitorContext), null);
137             rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null);
138             rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null);
139             rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null);
140         }
141
142         static string LoadObjectFromXaml(string animationXamlPath)
143         {
144             string xaml;
145             if (File.Exists(animationXamlPath))
146             {
147                 StreamReader reader = new StreamReader(animationXamlPath);
148                 xaml = reader.ReadToEnd();
149                 return xaml;
150             }
151
152             return null;
153         }
154         //if the assembly was generated using a version of XamlG that doesn't outputs XamlResourceIdAttributes, we still need to find the resource, and load it
155
156         //        static string GetXamlForType(Type type)
157         //        {
158         //            //the Previewer might want to provide it's own xaml for this... let them do that
159         //            //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
160
161         //            string xaml;
162         //#pragma warning disable 0618
163         //            if (ResourceLoader.ResourceProvider == null && (xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type)) != null)
164         //                return xaml;
165         //#pragma warning restore 0618
166
167         //            var assembly = type.GetTypeInfo().Assembly;
168         //            var resourceId = XamlResourceIdAttribute.GetResourceIdForType(type);
169
170         //            if (resourceId == null)
171         //                return LegacyGetXamlForType(type);
172
173         //            using (var stream = assembly.GetManifestResourceStream(resourceId))
174         //            {
175         //                if (stream != null)
176         //                    using (var reader = new StreamReader(stream))
177         //                        xaml = reader.ReadToEnd();
178         //                else
179         //                    xaml = null;
180         //            }
181
182         //            var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(assembly.GetName(), XamlResourceIdAttribute.GetPathForType(type));
183         //            return alternateXaml ?? xaml;
184         //        }
185
186         static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
187         static string LegacyGetXamlForType(Type type)
188         {
189             var assembly = type.GetTypeInfo().Assembly;
190
191             string resourceId;
192             if (XamlResources.TryGetValue(type, out resourceId)) {
193                 var result = ReadResourceAsXaml(type, assembly, resourceId);
194                 if (result != null)
195                     return result;
196             }
197
198             var likelyResourceName = type.Name + ".xaml";
199             var resourceNames = assembly.GetManifestResourceNames();
200             string resourceName = null;
201
202             // first pass, pray to find it because the user named it correctly
203
204             foreach (var resource in resourceNames) {
205                 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
206                     resourceName = resource;
207                     var xaml = ReadResourceAsXaml(type, assembly, resource);
208                     if (xaml != null)
209                         return xaml;
210                 }
211             }
212
213             // okay maybe they at least named it .xaml
214
215             foreach (var resource in resourceNames) {
216                 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
217                     continue;
218
219                 resourceName = resource;
220                 var xaml = ReadResourceAsXaml(type, assembly, resource);
221                 if (xaml != null)
222                     return xaml;
223             }
224
225             foreach (var resource in resourceNames) {
226                 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
227                     continue;
228
229                 resourceName = resource;
230                 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
231                 if (xaml != null)
232                     return xaml;
233             }
234
235             return null;
236         }
237
238         //legacy...
239         static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
240         {
241             try {
242                 var info = assembly.GetManifestResourceInfo(resource);
243
244                 if (!string.IsNullOrEmpty(info.FileName) &&
245                     string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
246                     return true;
247             }
248             catch (PlatformNotSupportedException) {
249                 // Because Win10 + .NET Native
250             }
251
252             if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
253                 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
254                 return true;
255
256             return false;
257         }
258
259         //part of the legacy as well...
260         static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
261         {
262             using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
263             using (var reader = new StreamReader(stream)) {
264                 if (validate) {
265                     // terrible validation of XML. Unfortunately it will probably work most of the time since comments
266                     // also start with a <. We can't bring in any real deps.
267
268                     var firstNonWhitespace = (char)reader.Read();
269                     while (char.IsWhiteSpace(firstNonWhitespace))
270                         firstNonWhitespace = (char)reader.Read();
271
272                     if (firstNonWhitespace != '<')
273                         return null;
274
275                     stream.Seek(0, SeekOrigin.Begin);
276                 }
277
278                 var xaml = reader.ReadToEnd();
279
280                 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
281                 var regex = new Regex(pattern, RegexOptions.ECMAScript);
282                 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
283                     return xaml;
284             }
285             return null;
286         }
287
288         public class RuntimeRootNode : RootNode
289         {
290             public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
291             {
292                 Root = root;
293             }
294
295             public object Root { get; internal set; }
296         }
297     }
298 }
299