2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 // Stephane Delcroix <stephane@mi8.be>
23 // Copyright (c) 2018 Mobile Inception
24 // Copyright (c) 2018-2014 Xamarin, Inc
26 // Permission is hereby granted, free of charge, to any person obtaining a copy
27 // of this software and associated documentation files (the "Software"), to deal
28 // in the Software without restriction, including without limitation the rights
29 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 // copies of the Software, and to permit persons to whom the Software is
31 // furnished to do so, subject to the following conditions:
33 // The above copyright notice and this permission notice shall be included in
34 // all copies or substantial portions of the Software.
36 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45 using System.Collections.Generic;
46 using System.ComponentModel;
47 using System.Diagnostics;
50 using System.Reflection;
51 using System.Text.RegularExpressions;
53 using Tizen.NUI.BaseComponents;
54 using Tizen.NUI.Binding;
55 using Tizen.NUI.Binding.Internals;
57 namespace Tizen.NUI.Xaml.Internals
59 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
60 [EditorBrowsable(EditorBrowsableState.Never)]
61 [Obsolete("Replaced by ResourceLoader")]
62 public static class XamlLoader
64 static Func<Type, string> xamlFileProvider;
66 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
67 [EditorBrowsable(EditorBrowsableState.Never)]
68 public static Func<Type, string> XamlFileProvider
70 get { return xamlFileProvider; }
73 xamlFileProvider = value;
74 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
75 //¯\_(??_/¯ the previewer forgot to set that bool
76 DoNotThrowOnExceptions = value != null;
80 internal static bool DoNotThrowOnExceptions { get; set; }
84 namespace Tizen.NUI.Xaml
86 static internal class XamlLoader
88 public static void Load(object view, Type callingType)
92 var xaml = GetXamlForType(callingType);
93 if (string.IsNullOrEmpty(xaml))
94 throw new XamlParseException(string.Format("Can't get xaml from type {0}", callingType), new XmlLineInfo());
96 NUIApplication.CurrentLoadedXaml = callingType.FullName;
98 catch (XamlParseException e)
100 Tizen.Log.Fatal("NUI", "XamlParseException e.Message: " + e.Message);
101 Console.WriteLine("\n[FATAL] XamlParseException e.Message: {0}\n", e.Message);
105 public static T LoadObject<T>(string path)
107 var xaml = GetAnimationXaml(path);
108 if (string.IsNullOrEmpty(xaml))
109 throw new XamlParseException(string.Format("No embeddedresource found for {0}", path), new XmlLineInfo());
110 Type type = typeof(T);
111 T ret = (T)type.Assembly.CreateInstance(type.FullName);
113 NameScopeExtensions.PushElement(ret);
115 using (var textReader = new StringReader(xaml))
116 using (var reader = XmlReader.Create(textReader))
118 while (reader.Read())
121 if (reader.NodeType == XmlNodeType.Whitespace)
123 if (reader.NodeType == XmlNodeType.XmlDeclaration)
125 if (reader.NodeType != XmlNodeType.Element)
127 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
131 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), ret, (IXmlNamespaceResolver)reader);
132 XamlParser.ParseXaml(rootnode, reader);
133 Visit(rootnode, new HydrationContext
136 #pragma warning disable 0618
137 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
138 #pragma warning restore 0618
144 NameScopeExtensions.PopElement();
148 public static void Load(object view, string xaml)
150 using (var textReader = new StringReader(xaml))
151 using (var reader = XmlReader.Create(textReader))
157 public static void Load(object view, XmlReader reader)
161 while (reader.Read())
164 if (reader.NodeType == XmlNodeType.Whitespace)
166 if (reader.NodeType == XmlNodeType.XmlDeclaration)
168 if (reader.NodeType != XmlNodeType.Element)
170 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
176 (view as Element).IsCreateByXaml = true;
179 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
180 XamlParser.ParseXaml(rootnode, reader);
181 Visit(rootnode, new HydrationContext
184 #pragma warning disable 0618
185 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
186 #pragma warning restore 0618
193 public static object Create(XmlReader reader, bool doNotThrow = false)
195 object inflatedView = null;
198 while (reader.Read())
201 if (reader.NodeType == XmlNodeType.Whitespace)
203 if (reader.NodeType == XmlNodeType.XmlDeclaration)
205 if (reader.NodeType != XmlNodeType.Element)
207 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
211 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
212 XamlParser.ParseXaml(rootnode, reader);
213 var visitorContext = new HydrationContext
215 ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
217 var cvv = new CreateValuesVisitor(visitorContext);
219 // Visit Parameter Properties to create instance from parameterized constructor
220 var type = XamlParser.GetElementType(rootnode.XmlType, rootnode, null, out XamlParseException xpe);
226 .DeclaredConstructors.FirstOrDefault(
228 ci.GetParameters().Length != 0 && ci.IsPublic &&
229 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute))));
230 if (ctorInfo != null)
232 foreach (var parameter in ctorInfo.GetParameters())
235 parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")?
236 .ConstructorArguments.First()
239 var name = new XmlName("", propname);
240 if (rootnode.Properties.TryGetValue(name, out INode node) && node is ValueNode)
242 node.Accept(cvv, rootnode);
248 cvv.Visit((ElementNode)rootnode, null);
249 inflatedView = rootnode.Root = visitorContext.Values[rootnode];
250 visitorContext.RootElement = inflatedView as BindableObject;
252 Visit(rootnode, visitorContext);
259 static void Visit(RootNode rootnode, HydrationContext visitorContext)
261 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
262 rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
263 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
264 rootnode.Accept(new NamescopingVisitor(visitorContext), null); //set namescopes for {x:Reference}
265 rootnode.Accept(new CreateValuesVisitor(visitorContext), null);
266 rootnode.Accept(new RegisterXNamesVisitor(visitorContext), null);
267 rootnode.Accept(new FillResourceDictionariesVisitor(visitorContext), null);
268 rootnode.Accept(new ApplyPropertiesVisitor(visitorContext, true), null);
271 static string GetAnimationXaml(string animationXamlPath)
274 if (File.Exists(animationXamlPath))
276 StreamReader reader = new StreamReader(animationXamlPath);
277 xaml = reader.ReadToEnd();
280 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
286 static string GetXamlForType(Type type)
288 //the Previewer might want to provide it's own xaml for this... let them do that
289 //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
292 string resourceName = type.Name + ".xaml";
293 string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
295 Tizen.Log.Fatal("NUI", "the resource path: " + resource);
296 int windowWidth = NUIApplication.GetDefaultWindow().Size.Width;
297 int windowHeight = NUIApplication.GetDefaultWindow().Size.Height;
299 string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
300 Tizen.Log.Fatal("NUI", "the resource path: " + likelyResourcePath);
302 if (!File.Exists(likelyResourcePath))
304 likelyResourcePath = resource + "layout/" + resourceName;
307 //Find the xaml file in the layout folder
308 if (File.Exists(likelyResourcePath))
310 StreamReader reader = new StreamReader(likelyResourcePath);
311 xaml = reader.ReadToEnd();
314 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
315 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
316 var regex = new Regex(pattern, RegexOptions.ECMAScript);
317 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
323 throw new XamlParseException(string.Format("Can't find type {0}", type.FullName), new XmlLineInfo());
328 Assembly assembly = type.Assembly;
330 var resourceId = XamlResourceIdAttribute.GetResourceIdForType(type);
331 if (null == resourceId)
333 throw new XamlParseException(string.Format("Can't find type {0} in embedded resource", type.FullName), new XmlLineInfo());
337 Stream stream = assembly.GetManifestResourceStream(resourceId);
341 Byte[] buffer = new byte[stream.Length];
342 stream.Read(buffer, 0, (int)stream.Length);
344 string ret = System.Text.Encoding.Default.GetString(buffer);
349 throw new XamlParseException(string.Format("Can't get xaml stream {0} in embedded resource", type.FullName), new XmlLineInfo());
355 //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
356 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
357 static string LegacyGetXamlForType(Type type)
359 var assembly = type.GetTypeInfo().Assembly;
362 if (XamlResources.TryGetValue(type, out resourceId))
364 var result = ReadResourceAsXaml(type, assembly, resourceId);
369 var likelyResourceName = type.Name + ".xaml";
370 var resourceNames = assembly.GetManifestResourceNames();
371 string resourceName = null;
373 // first pass, pray to find it because the user named it correctly
375 foreach (var resource in resourceNames)
377 if (ResourceMatchesFilename(assembly, resource, likelyResourceName))
379 resourceName = resource;
380 var xaml = ReadResourceAsXaml(type, assembly, resource);
386 // okay maybe they at least named it .xaml
388 foreach (var resource in resourceNames)
390 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
393 resourceName = resource;
394 var xaml = ReadResourceAsXaml(type, assembly, resource);
399 foreach (var resource in resourceNames)
401 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
404 resourceName = resource;
405 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
414 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
418 var info = assembly.GetManifestResourceInfo(resource);
420 if (!string.IsNullOrEmpty(info.FileName) &&
421 string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
424 catch (PlatformNotSupportedException)
426 // Because Win10 + .NET Native
429 if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
430 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
436 //part of the legacy as well...
437 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
439 using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
440 using (var reader = new StreamReader(stream))
444 // terrible validation of XML. Unfortunately it will probably work most of the time since comments
445 // also start with a <. We can't bring in any real deps.
447 var firstNonWhitespace = (char)reader.Read();
448 while (char.IsWhiteSpace(firstNonWhitespace))
449 firstNonWhitespace = (char)reader.Read();
451 if (firstNonWhitespace != '<')
454 stream.Seek(0, SeekOrigin.Begin);
457 var xaml = reader.ReadToEnd();
459 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
460 var regex = new Regex(pattern, RegexOptions.ECMAScript);
461 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
467 public class RuntimeRootNode : RootNode
469 public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base(xmlType, resolver)
474 public object Root { get; internal set; }
477 internal static string GetXamlForName(string nameOfXamlFile)
480 string resourceName = nameOfXamlFile + ".xaml";
481 string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
483 NUILog.Debug($"resource=({resource})");
485 int windowWidth = NUIApplication.GetDefaultWindow().Size.Width;
486 int windowHeight = NUIApplication.GetDefaultWindow().Size.Height;
488 string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
490 NUILog.Debug($"likelyResourcePath=({likelyResourcePath})");
493 if (!File.Exists(likelyResourcePath))
495 likelyResourcePath = resource + "layout/" + resourceName;
498 //Find the xaml file in the layout folder
499 if (File.Exists(likelyResourcePath))
501 StreamReader reader = new StreamReader(likelyResourcePath);
502 xaml = reader.ReadToEnd();
504 NUILog.Debug($"File is exist!, try with xaml: {xaml}");
507 var pattern = String.Format("x:Class *= *\"{0}\"", "Tizen.NUI.Layer");
508 var regex = new Regex(pattern, RegexOptions.ECMAScript);
509 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", "Tizen.NUI.Layer")))
515 pattern = String.Format("x:Class *= *\"{0}\"", "Tizen.NUI.BaseComponents.View");
516 regex = new Regex(pattern, RegexOptions.ECMAScript);
517 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", "Tizen.NUI.BaseComponents.View")))
524 throw new XamlParseException(string.Format("Can't find type {0}", "Tizen.NUI.XamlMainPage nor View nor Layer"), new XmlLineInfo());