5 // Stephane Delcroix <stephane@mi8.be>
7 // Copyright (c) 2018 Mobile Inception
8 // Copyright (c) 2018-2014 Xamarin, Inc
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 using System.Collections.Generic;
30 using System.ComponentModel;
31 using System.Diagnostics;
33 using System.Reflection;
34 using System.Text.RegularExpressions;
36 using Tizen.NUI.BaseComponents;
37 using Tizen.NUI.Binding;
38 using Tizen.NUI.Binding.Internals;
40 namespace Tizen.NUI.Xaml.Internals
42 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
43 [EditorBrowsable(EditorBrowsableState.Never)]
44 [Obsolete ("Replaced by ResourceLoader")]
45 public static class XamlLoader
47 static Func<Type, string> xamlFileProvider;
49 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
50 [EditorBrowsable(EditorBrowsableState.Never)]
51 public static Func<Type, string> XamlFileProvider {
52 get { return xamlFileProvider; }
55 xamlFileProvider = value;
56 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
57 //¯\_(??_/¯ the previewer forgot to set that bool
58 DoNotThrowOnExceptions = value != null;
62 internal static bool DoNotThrowOnExceptions { get; set; }
66 namespace Tizen.NUI.Xaml
68 static internal class XamlLoader
70 public static void Load(object view, Type callingType)
74 var xaml = GetXamlForType(callingType);
75 if (string.IsNullOrEmpty(xaml))
76 throw new XamlParseException(string.Format("Can't get xaml from type {0}", callingType), new XmlLineInfo());
79 catch (XamlParseException e)
81 Tizen.Log.Fatal("NUI", "XamlParseException e.Message: " + e.Message);
82 Console.WriteLine("\n[FATAL] XamlParseException e.Message: {0}\n", e.Message);
86 public static T LoadObject<T>(string path)
88 var xaml = GetAnimationXaml(path);
89 if (string.IsNullOrEmpty(xaml))
90 throw new XamlParseException(string.Format("No embeddedresource found for {0}", path), new XmlLineInfo());
91 Type type = typeof(T);
92 T ret = (T)type.Assembly.CreateInstance(type.FullName);
94 NameScopeExtensions.PushElement(ret);
96 using (var textReader = new StringReader(xaml))
97 using (var reader = XmlReader.Create(textReader))
102 if (reader.NodeType == XmlNodeType.Whitespace)
104 if (reader.NodeType == XmlNodeType.XmlDeclaration)
106 if (reader.NodeType != XmlNodeType.Element)
108 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
112 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), ret, (IXmlNamespaceResolver)reader);
113 XamlParser.ParseXaml(rootnode, reader);
114 Visit(rootnode, new HydrationContext
117 #pragma warning disable 0618
118 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
119 #pragma warning restore 0618
125 NameScopeExtensions.PopElement();
129 public static void Load(object view, string xaml)
131 using (var textReader = new StringReader(xaml))
132 using (var reader = XmlReader.Create(textReader))
134 while (reader.Read())
137 if (reader.NodeType == XmlNodeType.Whitespace)
139 if (reader.NodeType == XmlNodeType.XmlDeclaration)
141 if (reader.NodeType != XmlNodeType.Element)
143 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
149 (view as Element).IsCreateByXaml = true;
152 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
153 XamlParser.ParseXaml(rootnode, reader);
154 Visit(rootnode, new HydrationContext
157 #pragma warning disable 0618
158 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
159 #pragma warning restore 0618
166 [Obsolete("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
167 public static object Create(string xaml, bool doNotThrow = false)
169 object inflatedView = null;
170 using (var textreader = new StringReader(xaml))
171 using (var reader = XmlReader.Create(textreader))
173 while (reader.Read())
176 if (reader.NodeType == XmlNodeType.Whitespace)
178 if (reader.NodeType == XmlNodeType.XmlDeclaration)
180 if (reader.NodeType != XmlNodeType.Element)
182 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
186 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
187 XamlParser.ParseXaml(rootnode, reader);
188 var visitorContext = new HydrationContext
190 ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
192 var cvv = new CreateValuesVisitor(visitorContext);
193 cvv.Visit((ElementNode)rootnode, null);
194 inflatedView = rootnode.Root = visitorContext.Values[rootnode];
195 visitorContext.RootElement = inflatedView as BindableObject;
197 Visit(rootnode, visitorContext);
204 static void Visit(RootNode rootnode, HydrationContext visitorContext)
206 rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
207 rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null);
208 rootnode.Accept(new PruneIgnoredNodesVisitor(), null);
209 rootnode.Accept(new NamescopingVisitor(visitorContext), null); //set namescopes for {x:Reference}
210 rootnode.Accept(new CreateValuesVisitor(visitorContext), null);
211 rootnode.Accept(new RegisterXNamesVisitor(visitorContext), null);
212 rootnode.Accept(new FillResourceDictionariesVisitor(visitorContext), null);
213 rootnode.Accept(new ApplyPropertiesVisitor(visitorContext, true), null);
216 static string GetAnimationXaml(string animationXamlPath)
219 if (File.Exists(animationXamlPath))
221 StreamReader reader = new StreamReader(animationXamlPath);
222 xaml = reader.ReadToEnd();
225 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
231 static string GetXamlForType(Type type)
233 //the Previewer might want to provide it's own xaml for this... let them do that
234 //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
237 string resourceName = type.Name + ".xaml";
238 string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
240 Tizen.Log.Fatal("NUI", "the resource path: " + resource);
241 int windowWidth = Window.Instance.Size.Width;
242 int windowHeight = Window.Instance.Size.Height;
244 string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
245 Tizen.Log.Fatal("NUI", "the resource path: " + likelyResourcePath);
247 if (!File.Exists(likelyResourcePath))
249 likelyResourcePath = resource + "layout/" + resourceName;
252 //Find the xaml file in the layout folder
253 if (File.Exists(likelyResourcePath))
255 StreamReader reader = new StreamReader(likelyResourcePath);
256 xaml = reader.ReadToEnd();
259 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
260 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
261 var regex = new Regex(pattern, RegexOptions.ECMAScript);
262 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
268 throw new XamlParseException(string.Format("Can't find type {0}", type.FullName), new XmlLineInfo());
275 //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
276 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
277 static string LegacyGetXamlForType(Type type)
279 var assembly = type.GetTypeInfo().Assembly;
282 if (XamlResources.TryGetValue(type, out resourceId))
284 var result = ReadResourceAsXaml(type, assembly, resourceId);
289 var likelyResourceName = type.Name + ".xaml";
290 var resourceNames = assembly.GetManifestResourceNames();
291 string resourceName = null;
293 // first pass, pray to find it because the user named it correctly
295 foreach (var resource in resourceNames)
297 if (ResourceMatchesFilename(assembly, resource, likelyResourceName))
299 resourceName = resource;
300 var xaml = ReadResourceAsXaml(type, assembly, resource);
306 // okay maybe they at least named it .xaml
308 foreach (var resource in resourceNames)
310 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
313 resourceName = resource;
314 var xaml = ReadResourceAsXaml(type, assembly, resource);
319 foreach (var resource in resourceNames)
321 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
324 resourceName = resource;
325 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
334 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
338 var info = assembly.GetManifestResourceInfo(resource);
340 if (!string.IsNullOrEmpty(info.FileName) &&
341 string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
344 catch (PlatformNotSupportedException)
346 // Because Win10 + .NET Native
349 if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
350 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
356 //part of the legacy as well...
357 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
359 using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
360 using (var reader = new StreamReader(stream))
364 // terrible validation of XML. Unfortunately it will probably work most of the time since comments
365 // also start with a <. We can't bring in any real deps.
367 var firstNonWhitespace = (char)reader.Read();
368 while (char.IsWhiteSpace(firstNonWhitespace))
369 firstNonWhitespace = (char)reader.Read();
371 if (firstNonWhitespace != '<')
374 stream.Seek(0, SeekOrigin.Begin);
377 var xaml = reader.ReadToEnd();
379 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
380 var regex = new Regex(pattern, RegexOptions.ECMAScript);
381 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
387 public class RuntimeRootNode : RootNode
389 public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base(xmlType, resolver)
394 public object Root { get; internal set; }
397 internal static string GetXamlForName(string nameOfXamlFile)
400 string resourceName = nameOfXamlFile + ".xaml";
401 string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
403 NUILog.Debug($"resource=({resource})");
405 int windowWidth = Window.Instance.Size.Width;
406 int windowHeight = Window.Instance.Size.Height;
408 string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
410 NUILog.Debug($"likelyResourcePath=({likelyResourcePath})");
413 if (!File.Exists(likelyResourcePath))
415 likelyResourcePath = resource + "layout/" + resourceName;
418 //Find the xaml file in the layout folder
419 if (File.Exists(likelyResourcePath))
421 StreamReader reader = new StreamReader(likelyResourcePath);
422 xaml = reader.ReadToEnd();
424 NUILog.Debug($"File is exist!, try with xaml: {xaml}");
427 var pattern = String.Format("x:Class *= *\"{0}\"", "Tizen.NUI.Layer");
428 var regex = new Regex(pattern, RegexOptions.ECMAScript);
429 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", "Tizen.NUI.Layer")))
434 pattern = String.Format("x:Class *= *\"{0}\"", "Tizen.NUI.BaseComponents.View");
435 regex = new Regex(pattern, RegexOptions.ECMAScript);
436 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", "Tizen.NUI.BaseComponents.View")))
441 throw new XamlParseException(string.Format("Can't find type {0}", "Tizen.NUI.XamlMainPage nor View nor Layer"), new XmlLineInfo());