2 * Copyright(c) 2022 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.
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Diagnostics;
23 using System.Reflection;
24 using System.Text.RegularExpressions;
26 using Tizen.NUI.Binding;
27 using Tizen.NUI.Binding.Internals;
29 namespace Tizen.NUI.Xaml.Internals
31 [Obsolete ("Replaced by ResourceLoader")]
32 internal static class XamlLoader
34 static Func<Type, string> xamlFileProvider;
36 public static Func<Type, string> XamlFileProvider {
37 get { return xamlFileProvider; }
39 xamlFileProvider = value;
40 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
41 //¯\_(ツ)_/¯ the previewer forgot to set that bool
42 DoNotThrowOnExceptions = value != null;
46 internal static bool DoNotThrowOnExceptions { get; set; }
50 namespace Tizen.NUI.Xaml
52 static internal class XamlLoader
54 public static void Load(object view, string xaml)
56 using (var textReader = new StringReader(xaml))
57 using (var reader = XmlReader.Create(textReader))
62 if (reader.NodeType == XmlNodeType.Whitespace)
64 if (reader.NodeType == XmlNodeType.XmlDeclaration)
66 if (reader.NodeType != XmlNodeType.Element) {
67 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
71 for (int i = 0; i < 100; i++)
73 XmlType temp = new XmlType(reader.NamespaceURI, reader.Name, null);
76 XmlType xmlType = new XmlType(reader.NamespaceURI, reader.Name, null);
78 for (int i = 0; i < 100; i++)
80 new RuntimeRootNode(xmlType, view, (IXmlNamespaceResolver)reader);
83 var rootnode = new RuntimeRootNode (xmlType, view, (IXmlNamespaceResolver)reader);
84 XamlParser.ParseXaml (rootnode, reader);
85 Visit (rootnode, new HydrationContext {
87 #pragma warning disable 0618
88 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
89 #pragma warning restore 0618
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)
99 object inflatedView = null;
100 using (var textreader = new StringReader(xaml))
101 using (var reader = XmlReader.Create (textreader)) {
102 while (reader.Read ()) {
104 if (reader.NodeType == XmlNodeType.Whitespace)
106 if (reader.NodeType == XmlNodeType.XmlDeclaration)
108 if (reader.NodeType != XmlNodeType.Element) {
109 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
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,
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;
123 Visit (rootnode, visitorContext);
130 static void Visit (RootNode rootnode, HydrationContext visitorContext)
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);
142 static string LoadObjectFromXaml(string animationXamlPath)
145 if (File.Exists(animationXamlPath))
147 StreamReader reader = new StreamReader(animationXamlPath);
148 xaml = reader.ReadToEnd();
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
156 // static string GetXamlForType(Type type)
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
162 //#pragma warning disable 0618
163 // if (ResourceLoader.ResourceProvider == null && (xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type)) != null)
165 //#pragma warning restore 0618
167 // var assembly = type.GetTypeInfo().Assembly;
168 // var resourceId = XamlResourceIdAttribute.GetResourceIdForType(type);
170 // if (resourceId == null)
171 // return LegacyGetXamlForType(type);
173 // using (var stream = assembly.GetManifestResourceStream(resourceId))
175 // if (stream != null)
176 // using (var reader = new StreamReader(stream))
177 // xaml = reader.ReadToEnd();
182 // var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(assembly.GetName(), XamlResourceIdAttribute.GetPathForType(type));
183 // return alternateXaml ?? xaml;
186 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
187 static string LegacyGetXamlForType(Type type)
189 var assembly = type.GetTypeInfo().Assembly;
192 if (XamlResources.TryGetValue(type, out resourceId)) {
193 var result = ReadResourceAsXaml(type, assembly, resourceId);
198 var likelyResourceName = type.Name + ".xaml";
199 var resourceNames = assembly.GetManifestResourceNames();
200 string resourceName = null;
202 // first pass, pray to find it because the user named it correctly
204 foreach (var resource in resourceNames) {
205 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
206 resourceName = resource;
207 var xaml = ReadResourceAsXaml(type, assembly, resource);
213 // okay maybe they at least named it .xaml
215 foreach (var resource in resourceNames) {
216 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
219 resourceName = resource;
220 var xaml = ReadResourceAsXaml(type, assembly, resource);
225 foreach (var resource in resourceNames) {
226 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
229 resourceName = resource;
230 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
239 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
242 var info = assembly.GetManifestResourceInfo(resource);
244 if (!string.IsNullOrEmpty(info.FileName) &&
245 string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
248 catch (PlatformNotSupportedException) {
249 // Because Win10 + .NET Native
252 if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
253 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
259 //part of the legacy as well...
260 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
262 using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
263 using (var reader = new StreamReader(stream)) {
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.
268 var firstNonWhitespace = (char)reader.Read();
269 while (char.IsWhiteSpace(firstNonWhitespace))
270 firstNonWhitespace = (char)reader.Read();
272 if (firstNonWhitespace != '<')
275 stream.Seek(0, SeekOrigin.Begin);
278 var xaml = reader.ReadToEnd();
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)))
288 public class RuntimeRootNode : RootNode
290 public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
295 public object Root { get; internal set; }