summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Xaml/XamlLoader.cs
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Xaml/XamlLoader.cs
downloadxamarin-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.cs208
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