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 [Obsolete ("Replaced by ResourceLoader")]
43 internal static class XamlLoader
45 static Func<Type, string> xamlFileProvider;
47 public static Func<Type, string> XamlFileProvider {
48 get { return xamlFileProvider; }
50 xamlFileProvider = value;
51 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
52 //¯\_(ツ)_/¯ the previewer forgot to set that bool
53 DoNotThrowOnExceptions = value != null;
57 internal static bool DoNotThrowOnExceptions { get; set; }
61 namespace Tizen.NUI.Xaml
63 static internal class XamlLoader
65 public static void Load(object view, Type callingType)
69 var xaml = GetXamlForType(callingType);
70 if (string.IsNullOrEmpty(xaml))
71 throw new XamlParseException(string.Format("Can't get xaml from type {0}", callingType), new XmlLineInfo());
74 catch (XamlParseException e)
76 Tizen.Log.Fatal("NUI", "XamlParseException e.Message: " + e.Message);
77 Console.WriteLine("\n[FATAL] XamlParseException e.Message: {0}\n", e.Message);
81 public static T LoadObject<T>(string path)
83 var xaml = GetAnimationXaml(path);
84 if (string.IsNullOrEmpty(xaml))
85 throw new XamlParseException(string.Format("No embeddedresource found for {0}", path), new XmlLineInfo());
86 Type type = typeof(T);
87 T ret = (T)type.Assembly.CreateInstance(type.FullName);
89 NameScopeExtensions.PushElement(ret);
91 using (var textReader = new StringReader(xaml))
92 using (var reader = XmlReader.Create(textReader))
97 if (reader.NodeType == XmlNodeType.Whitespace)
99 if (reader.NodeType == XmlNodeType.XmlDeclaration)
101 if (reader.NodeType != XmlNodeType.Element)
103 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
107 var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), ret, (IXmlNamespaceResolver)reader);
108 XamlParser.ParseXaml(rootnode, reader);
109 Visit(rootnode, new HydrationContext
112 #pragma warning disable 0618
113 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { } : (Action<Exception>)null)
114 #pragma warning restore 0618
120 NameScopeExtensions.PopElement();
124 public static void Load(object view, string xaml)
126 using (var textReader = new StringReader(xaml))
127 using (var reader = XmlReader.Create(textReader))
129 while (reader.Read())
132 if (reader.NodeType == XmlNodeType.Whitespace)
134 if (reader.NodeType == XmlNodeType.XmlDeclaration)
136 if (reader.NodeType != XmlNodeType.Element) {
137 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
143 (view as Element).IsCreateByXaml = true;
146 var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
147 XamlParser.ParseXaml (rootnode, reader);
148 Visit (rootnode, new HydrationContext {
150 #pragma warning disable 0618
151 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
152 #pragma warning restore 0618
159 [Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
160 public static object Create (string xaml, bool doNotThrow = false)
162 object inflatedView = null;
163 using (var textreader = new StringReader(xaml))
164 using (var reader = XmlReader.Create (textreader)) {
165 while (reader.Read ()) {
167 if (reader.NodeType == XmlNodeType.Whitespace)
169 if (reader.NodeType == XmlNodeType.XmlDeclaration)
171 if (reader.NodeType != XmlNodeType.Element) {
172 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
176 var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
177 XamlParser.ParseXaml (rootnode, reader);
178 var visitorContext = new HydrationContext {
179 ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
181 var cvv = new CreateValuesVisitor (visitorContext);
182 cvv.Visit ((ElementNode)rootnode, null);
183 inflatedView = rootnode.Root = visitorContext.Values [rootnode];
184 visitorContext.RootElement = inflatedView as BindableObject;
186 Visit (rootnode, visitorContext);
193 static void Visit (RootNode rootnode, HydrationContext visitorContext)
195 rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
196 rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null);
197 rootnode.Accept (new PruneIgnoredNodesVisitor(), null);
198 rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference}
199 rootnode.Accept (new CreateValuesVisitor (visitorContext), null);
200 rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null);
201 rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null);
202 rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null);
205 static string GetAnimationXaml(string animationXamlPath)
208 if (File.Exists(animationXamlPath))
210 StreamReader reader = new StreamReader(animationXamlPath);
211 xaml = reader.ReadToEnd();
214 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
220 static string GetXamlForType(Type type)
222 //the Previewer might want to provide it's own xaml for this... let them do that
223 //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
226 string resourceName = type.Name + ".xaml";
227 string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
229 Tizen.Log.Fatal("NUI", "the resource path: " + resource);
230 int windowWidth = Window.Instance.Size.Width;
231 int windowHeight = Window.Instance.Size.Height;
233 string likelyResourcePath = resource + "layout/" + windowWidth.ToString() + "x" + windowHeight.ToString() + "/" + resourceName;
234 Tizen.Log.Fatal("NUI", "the resource path: " + likelyResourcePath);
236 if (!File.Exists(likelyResourcePath))
238 likelyResourcePath = resource + "layout/" + resourceName;
241 //Find the xaml file in the layout folder
242 if (File.Exists(likelyResourcePath))
244 StreamReader reader = new StreamReader(likelyResourcePath);
245 xaml = reader.ReadToEnd();
248 Tizen.Log.Fatal("NUI", "File is exist!, try with xaml: " + xaml);
249 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
250 var regex = new Regex(pattern, RegexOptions.ECMAScript);
251 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
257 throw new XamlParseException(string.Format("Can't find type {0}", type.FullName), new XmlLineInfo());
264 //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
265 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
266 static string LegacyGetXamlForType(Type type)
268 var assembly = type.GetTypeInfo().Assembly;
271 if (XamlResources.TryGetValue(type, out resourceId)) {
272 var result = ReadResourceAsXaml(type, assembly, resourceId);
277 var likelyResourceName = type.Name + ".xaml";
278 var resourceNames = assembly.GetManifestResourceNames();
279 string resourceName = null;
281 // first pass, pray to find it because the user named it correctly
283 foreach (var resource in resourceNames) {
284 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
285 resourceName = resource;
286 var xaml = ReadResourceAsXaml(type, assembly, resource);
292 // okay maybe they at least named it .xaml
294 foreach (var resource in resourceNames) {
295 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
298 resourceName = resource;
299 var xaml = ReadResourceAsXaml(type, assembly, resource);
304 foreach (var resource in resourceNames) {
305 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
308 resourceName = resource;
309 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
318 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
321 var info = assembly.GetManifestResourceInfo(resource);
323 if (!string.IsNullOrEmpty(info.FileName) &&
324 string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
327 catch (PlatformNotSupportedException) {
328 // Because Win10 + .NET Native
331 if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
332 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
338 //part of the legacy as well...
339 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
341 using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
342 using (var reader = new StreamReader(stream)) {
344 // terrible validation of XML. Unfortunately it will probably work most of the time since comments
345 // also start with a <. We can't bring in any real deps.
347 var firstNonWhitespace = (char)reader.Read();
348 while (char.IsWhiteSpace(firstNonWhitespace))
349 firstNonWhitespace = (char)reader.Read();
351 if (firstNonWhitespace != '<')
354 stream.Seek(0, SeekOrigin.Begin);
357 var xaml = reader.ReadToEnd();
359 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
360 var regex = new Regex(pattern, RegexOptions.ECMAScript);
361 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
367 public class RuntimeRootNode : RootNode
369 public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
374 public object Root { get; internal set; }