diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Xaml/XamlLoader.cs | |
download | xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2 xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip |
Initial import
Diffstat (limited to 'Xamarin.Forms.Xaml/XamlLoader.cs')
-rw-r--r-- | Xamarin.Forms.Xaml/XamlLoader.cs | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/Xamarin.Forms.Xaml/XamlLoader.cs b/Xamarin.Forms.Xaml/XamlLoader.cs new file mode 100644 index 00000000..6d7674b9 --- /dev/null +++ b/Xamarin.Forms.Xaml/XamlLoader.cs @@ -0,0 +1,208 @@ +// +// XamlLoader.cs +// +// Author: +// Stephane Delcroix <stephane@mi8.be> +// +// Copyright (c) 2013 Mobile Inception +// Copyright (c) 2013-2014 Xamarin, Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Xml; + +namespace Xamarin.Forms.Xaml +{ + internal static class XamlLoader + { + static readonly Dictionary<Type, string> XamlResources = new Dictionary<Type, string>(); + + public static void Load(BindableObject view, Type callingType) + { + var xaml = GetXamlForType(callingType); + if (string.IsNullOrEmpty(xaml)) + throw new XamlParseException(string.Format("No embeddedresources found for {0}", callingType), new XmlLineInfo()); + Load(view, xaml); + } + + public static void Load(BindableObject view, string xaml) + { + using(var reader = XmlReader.Create(new StringReader(xaml))) + { + while (reader.Read()) + { + //Skip until element + if (reader.NodeType == XmlNodeType.Whitespace) + continue; + if (reader.NodeType != XmlNodeType.Element) + { + Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value); + continue; + } + + var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), view); + + XamlParser.ParseXaml(rootnode, reader); + + var visitorContext = new HydratationContext { RootElement = view }; + + rootnode.Accept(new XamlNodeVisitor((node, parent) => node.Parent = parent), null); + //set parents for {StaticResource} + rootnode.Accept(new ExpandMarkupsVisitor(visitorContext), null); + rootnode.Accept(new NamescopingVisitor(visitorContext), null); //set namescopes for {x:Reference} + rootnode.Accept(new CreateValuesVisitor(visitorContext), null); + rootnode.Accept(new RegisterXNamesVisitor(visitorContext), null); + rootnode.Accept(new FillResourceDictionariesVisitor(visitorContext), null); + rootnode.Accept(new ApplyPropertiesVisitor(visitorContext, true), null); + break; + } + } + } + + static string GetXamlForType(Type type) + { + var assembly = type.GetTypeInfo().Assembly; + + string resourceId; + if (XamlResources.TryGetValue(type, out resourceId)) + { + var result = ReadResourceAsXaml(type, assembly, resourceId); + if (result != null) + return result; + } + + var likelyResourceName = type.Name + ".xaml"; + var resourceNames = assembly.GetManifestResourceNames(); + string resourceName = null; + + // first pass, pray to find it because the user named it correctly + + string xaml = null; + foreach (var resource in resourceNames) + { + if (ResourceMatchesFilename(assembly, resource, likelyResourceName)) + { + resourceName = resource; + xaml = ReadResourceAsXaml(type, assembly, resource); + if (xaml != null) + goto end; + } + } + + // okay maybe they at least named it .xaml + + foreach (var resource in resourceNames) + { + if (!resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + continue; + + resourceName = resource; + xaml = ReadResourceAsXaml(type, assembly, resource); + if (xaml != null) + goto end; + } + + foreach (var resource in resourceNames) + { + if (resource.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + continue; + + resourceName = resource; + xaml = ReadResourceAsXaml(type, assembly, resource, true); + if (xaml != null) + goto end; + } + + end: + if (xaml == null) + return null; + + XamlResources[type] = resourceName; + return xaml; + } + + static bool ResourceMatchesFilename(Assembly assembly, string resource, string filename) + { + try + { + var info = assembly.GetManifestResourceInfo(resource); + + if (!string.IsNullOrEmpty(info.FileName) && + string.Compare(info.FileName, filename, StringComparison.OrdinalIgnoreCase) == 0) + return true; + } + catch (PlatformNotSupportedException) + { + // Because Win10 + .NET Native + } + + if (resource.EndsWith("." + filename, StringComparison.OrdinalIgnoreCase) || + string.Compare(resource, filename, StringComparison.OrdinalIgnoreCase) == 0) + return true; + + return false; + } + + static string ReadResourceAsXaml(Type type, Assembly assembly, string likelyTargetName, bool validate = false) + { + using(var stream = assembly.GetManifestResourceStream(likelyTargetName)) + using(var reader = new StreamReader(stream)) + { + if (validate) + { + // terrible validation of XML. Unfortunately it will probably work most of the time since comments + // also start with a <. We can't bring in any real deps. + + var firstNonWhitespace = (char)reader.Read(); + while (char.IsWhiteSpace(firstNonWhitespace)) + firstNonWhitespace = (char)reader.Read(); + + if (firstNonWhitespace != '<') + return null; + + stream.Seek(0, SeekOrigin.Begin); + } + + var xaml = reader.ReadToEnd(); + + var pattern = String.Format("x:Class *= *\"{0}\"", type.FullName); + var regex = new Regex(pattern, RegexOptions.ECMAScript); + if (regex.IsMatch(xaml) || xaml.Contains(String.Format("x:Class=\"{0}\"", type.FullName))) + return xaml; + } + return null; + } + + public class RuntimeRootNode : RootNode + { + public RuntimeRootNode(XmlType xmlType, object root) : base(xmlType) + { + Root = root; + } + + public object Root { get; private set; } + } + } +}
\ No newline at end of file |