5 // Stephane Delcroix <stephane@mi8.be>
7 // Copyright (c) 2013 Mobile Inception
8 // Copyright (c) 2013-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.Diagnostics;
32 using System.Reflection;
33 using System.Text.RegularExpressions;
35 using Tizen.NUI.Binding;
36 using Tizen.NUI.Internals;
38 namespace Tizen.NUI.Xaml.Internals
40 [Obsolete ("Replaced by ResourceLoader")]
41 internal static class XamlLoader
43 static Func<Type, string> xamlFileProvider;
45 public static Func<Type, string> XamlFileProvider {
46 get { return xamlFileProvider; }
48 xamlFileProvider = value;
49 Tizen.NUI.Xaml.DesignMode.IsDesignModeEnabled = true;
50 //¯\_(ツ)_/¯ the previewer forgot to set that bool
51 DoNotThrowOnExceptions = value != null;
55 internal static bool DoNotThrowOnExceptions { get; set; }
59 namespace Tizen.NUI.Xaml
61 static class XamlLoader
63 public static void Load(object view, Type callingType)
65 var xaml = GetXamlForType(callingType);
66 if (string.IsNullOrEmpty(xaml))
67 throw new XamlParseException(string.Format("No embeddedresource found for {0}", callingType), new XmlLineInfo());
68 Console.WriteLine("============= Got xaml text is {0} ===========", xaml);
72 public static void Load(object view, string xaml)
74 using (var textReader = new StringReader(xaml))
75 using (var reader = XmlReader.Create(textReader))
80 if (reader.NodeType == XmlNodeType.Whitespace)
82 if (reader.NodeType == XmlNodeType.XmlDeclaration)
84 if (reader.NodeType != XmlNodeType.Element) {
85 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
89 var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader);
90 XamlParser.ParseXaml (rootnode, reader);
91 Visit (rootnode, new HydrationContext {
93 #pragma warning disable 0618
94 ExceptionHandler = ResourceLoader.ExceptionHandler ?? (Internals.XamlLoader.DoNotThrowOnExceptions ? e => { }: (Action<Exception>)null)
95 #pragma warning restore 0618
102 [Obsolete ("Use the XamlFileProvider to provide xaml files. We will remove this when Cycle 8 hits Stable.")]
103 public static object Create (string xaml, bool doNotThrow = false)
105 object inflatedView = null;
106 using (var textreader = new StringReader(xaml))
107 using (var reader = XmlReader.Create (textreader)) {
108 while (reader.Read ()) {
110 if (reader.NodeType == XmlNodeType.Whitespace)
112 if (reader.NodeType == XmlNodeType.XmlDeclaration)
114 if (reader.NodeType != XmlNodeType.Element) {
115 Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value);
119 var rootnode = new RuntimeRootNode (new XmlType (reader.NamespaceURI, reader.Name, null), null, (IXmlNamespaceResolver)reader);
120 XamlParser.ParseXaml (rootnode, reader);
121 var visitorContext = new HydrationContext {
122 ExceptionHandler = doNotThrow ? e => { } : (Action<Exception>)null,
124 var cvv = new CreateValuesVisitor (visitorContext);
125 cvv.Visit ((ElementNode)rootnode, null);
126 inflatedView = rootnode.Root = visitorContext.Values [rootnode];
127 visitorContext.RootElement = inflatedView as BindableObject;
129 Visit (rootnode, visitorContext);
136 static void Visit (RootNode rootnode, HydrationContext visitorContext)
138 rootnode.Accept (new XamlNodeVisitor ((node, parent) => node.Parent = parent), null); //set parents for {StaticResource}
139 rootnode.Accept (new ExpandMarkupsVisitor (visitorContext), null);
140 rootnode.Accept (new PruneIgnoredNodesVisitor(), null);
141 rootnode.Accept (new NamescopingVisitor (visitorContext), null); //set namescopes for {x:Reference}
142 rootnode.Accept (new CreateValuesVisitor (visitorContext), null);
143 rootnode.Accept (new RegisterXNamesVisitor (visitorContext), null);
144 rootnode.Accept (new FillResourceDictionariesVisitor (visitorContext), null);
145 rootnode.Accept (new ApplyPropertiesVisitor (visitorContext, true), null);
148 static string GetXamlForType(Type type)
150 //the Previewer might want to provide it's own xaml for this... let them do that
151 //the check at the end is preferred (using ResourceLoader). keep this until all the previewers are updated
154 #pragma warning disable 0618
155 if (ResourceLoader.ResourceProvider == null && (xaml = Internals.XamlLoader.XamlFileProvider?.Invoke(type)) != null)
157 #pragma warning restore 0618
159 var assembly = type.GetTypeInfo().Assembly;
160 var resourceId = XamlResourceIdAttribute.GetResourceIdForType(type);
162 if (resourceId == null)
163 return LegacyGetXamlForType(type);
165 using (var stream = assembly.GetManifestResourceStream(resourceId)) {
167 using (var reader = new StreamReader(stream))
168 xaml = reader.ReadToEnd();
173 var alternateXaml = ResourceLoader.ResourceProvider?.Invoke(assembly.GetName(), XamlResourceIdAttribute.GetPathForType(type));
174 return alternateXaml ?? xaml;
177 //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
178 static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>();
179 static string LegacyGetXamlForType(Type type)
181 var assembly = type.GetTypeInfo().Assembly;
184 if (XamlResources.TryGetValue(type, out resourceId)) {
185 var result = ReadResourceAsXaml(type, assembly, resourceId);
190 var likelyResourceName = type.Name + ".xaml";
191 var resourceNames = assembly.GetManifestResourceNames();
192 string resourceName = null;
194 // first pass, pray to find it because the user named it correctly
196 foreach (var resource in resourceNames) {
197 if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) {
198 resourceName = resource;
199 var xaml = ReadResourceAsXaml(type, assembly, resource);
205 // okay maybe they at least named it .xaml
207 foreach (var resource in resourceNames) {
208 if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
211 resourceName = resource;
212 var xaml = ReadResourceAsXaml(type, assembly, resource);
217 foreach (var resource in resourceNames) {
218 if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
221 resourceName = resource;
222 var xaml = ReadResourceAsXaml(type, assembly, resource, true);
231 static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename)
234 var info = assembly.GetManifestResourceInfo(resource);
236 if (!string.IsNullOrEmpty(info.FileName) &&
237 string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0)
240 catch (PlatformNotSupportedException) {
241 // Because Win10 + .NET Native
244 if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) ||
245 string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0)
251 //part of the legacy as well...
252 static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false)
254 using (var stream = assembly.GetManifestResourceStream(likelyTargetName))
255 using (var reader = new StreamReader(stream)) {
257 // terrible validation of XML. Unfortunately it will probably work most of the time since comments
258 // also start with a <. We can't bring in any real deps.
260 var firstNonWhitespace = (char)reader.Read();
261 while (char.IsWhiteSpace(firstNonWhitespace))
262 firstNonWhitespace = (char)reader.Read();
264 if (firstNonWhitespace != '<')
267 stream.Seek(0, SeekOrigin.Begin);
270 var xaml = reader.ReadToEnd();
272 var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName);
273 var regex = new Regex(pattern, RegexOptions.ECMAScript);
274 if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName)))
280 public class RuntimeRootNode : RootNode
282 public RuntimeRootNode(XmlType xmlType, object root, IXmlNamespaceResolver resolver) : base (xmlType, resolver)
287 public object Root { get; internal set; }