summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Build.Tasks/XamlGTask.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Build.Tasks/XamlGTask.cs')
-rw-r--r--Xamarin.Forms.Build.Tasks/XamlGTask.cs261
1 files changed, 261 insertions, 0 deletions
diff --git a/Xamarin.Forms.Build.Tasks/XamlGTask.cs b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
new file mode 100644
index 00000000..28158e6b
--- /dev/null
+++ b/Xamarin.Forms.Build.Tasks/XamlGTask.cs
@@ -0,0 +1,261 @@
+using System;
+using System.CodeDom;
+using System.CodeDom.Compiler;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.CSharp;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Build.Tasks
+{
+ public class XamlGTask : Task
+ {
+ internal static CodeDomProvider Provider = new CSharpCodeProvider();
+
+ [Required]
+ public string Source { get; set; }
+
+ public string Language { get; set; }
+
+ public string AssemblyName { get; set; }
+
+ [Output]
+ public string OutputFile { get; set; }
+
+ public override bool Execute()
+ {
+ if (Source == null || OutputFile == null)
+ {
+ Log.LogMessage("Skipping XamlG");
+ return true;
+ }
+
+ Log.LogMessage("Source: {0}", Source);
+ Log.LogMessage("Language: {0}", Language);
+ Log.LogMessage("AssemblyName: {0}", AssemblyName);
+ Log.LogMessage("OutputFile {0}", OutputFile);
+
+ try
+ {
+ GenerateFile(Source, OutputFile);
+ return true;
+ }
+ catch (XmlException xe)
+ {
+ Log.LogError(null, null, null, Source, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
+
+ return false;
+ }
+ catch (Exception e)
+ {
+ Log.LogError(null, null, null, Source, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+ return false;
+ }
+ }
+
+ internal static void ParseXaml(TextReader xaml, out string rootType, out string rootNs, out CodeTypeReference baseType,
+ out IDictionary<string, CodeTypeReference> namesAndTypes)
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(xaml);
+
+ var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
+ nsmgr.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
+ nsmgr.AddNamespace("x2009", "http://schemas.microsoft.com/winfx/2009/xaml");
+ nsmgr.AddNamespace("f", "http://xamarin.com/schemas/2014/forms");
+
+ var root = xmlDoc.SelectSingleNode("/*", nsmgr);
+ if (root == null)
+ {
+ Console.Error.WriteLine("{0}: No root node found");
+ rootType = null;
+ rootNs = null;
+ baseType = null;
+ namesAndTypes = null;
+ return;
+ }
+
+ var rootClass = root.Attributes["Class", "http://schemas.microsoft.com/winfx/2006/xaml"] ??
+ root.Attributes["Class", "http://schemas.microsoft.com/winfx/2009/xaml"];
+ if (rootClass == null)
+ {
+ rootType = null;
+ rootNs = null;
+ baseType = null;
+ namesAndTypes = null;
+ return;
+ }
+
+ string rootAsm;
+ XmlnsHelper.ParseXmlns(rootClass.Value, out rootType, out rootNs, out rootAsm);
+ namesAndTypes = GetNamesAndTypes(root, nsmgr);
+
+ var typeArguments = root.Attributes["TypeArguments", "http://schemas.microsoft.com/winfx/2009/xaml"];
+
+ baseType = GetType(root.NamespaceURI, root.LocalName, typeArguments == null ? null : typeArguments.Value.Split(','),
+ root.GetNamespaceOfPrefix);
+ }
+
+ internal static void GenerateCode(string rootType, string rootNs, CodeTypeReference baseType,
+ IDictionary<string, CodeTypeReference> namesAndTypes, string outFile)
+ {
+ if (rootType == null)
+ {
+ File.WriteAllText(outFile, "");
+ return;
+ }
+
+ var ccu = new CodeCompileUnit();
+ var declNs = new CodeNamespace(rootNs);
+ ccu.Namespaces.Add(declNs);
+
+ declNs.Imports.Add(new CodeNamespaceImport("System"));
+ declNs.Imports.Add(new CodeNamespaceImport("Xamarin.Forms"));
+ declNs.Imports.Add(new CodeNamespaceImport("Xamarin.Forms.Xaml"));
+
+ var declType = new CodeTypeDeclaration(rootType);
+ declType.IsPartial = true;
+ declType.BaseTypes.Add(baseType);
+
+ declNs.Types.Add(declType);
+
+ var initcomp = new CodeMemberMethod
+ {
+ Name = "InitializeComponent",
+ CustomAttributes =
+ {
+ new CodeAttributeDeclaration(new CodeTypeReference(typeof (GeneratedCodeAttribute)),
+ new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
+ new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")))
+ }
+ };
+ declType.Members.Add(initcomp);
+
+ initcomp.Statements.Add(new CodeMethodInvokeExpression(
+ new CodeThisReferenceExpression(),
+ "LoadFromXaml", new CodeTypeOfExpression(declType.Name)));
+
+ foreach (var entry in namesAndTypes)
+ {
+ string name = entry.Key;
+ var type = entry.Value;
+
+ var field = new CodeMemberField
+ {
+ Name = name,
+ Type = type,
+ CustomAttributes =
+ {
+ new CodeAttributeDeclaration(new CodeTypeReference(typeof (GeneratedCodeAttribute)),
+ new CodeAttributeArgument(new CodePrimitiveExpression("Xamarin.Forms.Build.Tasks.XamlG")),
+ new CodeAttributeArgument(new CodePrimitiveExpression("0.0.0.0")))
+ }
+ };
+
+ declType.Members.Add(field);
+
+ var find_invoke = new CodeMethodInvokeExpression(
+ new CodeMethodReferenceExpression(
+ new CodeThisReferenceExpression(),
+ "FindByName", type), new CodePrimitiveExpression(name));
+
+ //CodeCastExpression cast = new CodeCastExpression (type, find_invoke);
+
+ CodeAssignStatement assign = new CodeAssignStatement(
+ new CodeVariableReferenceExpression(name), find_invoke);
+
+ initcomp.Statements.Add(assign);
+ }
+
+ using(var writer = new StreamWriter(outFile))
+ Provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions());
+ }
+
+ internal static void GenerateFile(string xamlFile, string outFile)
+ {
+ string rootType, rootNs;
+ CodeTypeReference baseType;
+ IDictionary<string, CodeTypeReference> namesAndTypes;
+ using(StreamReader reader = File.OpenText(xamlFile))
+ ParseXaml(reader, out rootType, out rootNs, out baseType, out namesAndTypes);
+ GenerateCode(rootType, rootNs, baseType, namesAndTypes, outFile);
+ }
+
+ static Dictionary<string, CodeTypeReference> GetNamesAndTypes(XmlNode root, XmlNamespaceManager nsmgr)
+ {
+ var res = new Dictionary<string, CodeTypeReference>();
+
+ foreach (string attrib in new[] { "x:Name", "x2009:Name" })
+ {
+ XmlNodeList names =
+ root.SelectNodes(
+ "//*[@" + attrib +
+ "][not(ancestor:: f:DataTemplate) and not(ancestor:: f:ControlTemplate) and not(ancestor:: f:Style)]", nsmgr);
+ foreach (XmlNode node in names)
+ {
+ // Don't take the root canvas
+ if (node == root)
+ continue;
+
+ XmlAttribute attr = node.Attributes["Name", "http://schemas.microsoft.com/winfx/2006/xaml"] ??
+ node.Attributes["Name", "http://schemas.microsoft.com/winfx/2009/xaml"];
+ XmlAttribute typeArgsAttr = node.Attributes["x:TypeArguments"];
+ var typeArgsList = typeArgsAttr == null ? null : typeArgsAttr.Value.Split(',').ToList();
+ string name = attr.Value;
+
+ res[name] = GetType(node.NamespaceURI, node.LocalName, typeArgsList, node.GetNamespaceOfPrefix);
+ }
+ }
+
+ return res;
+ }
+
+ static CodeTypeReference GetType(string nsuri, string type, IList<string> typeArguments = null,
+ Func<string, string> getNamespaceOfPrefix = null)
+ {
+ var ns = GetNamespace(nsuri);
+ if (ns != null)
+ type = String.Concat(ns, ".", type);
+
+ if (typeArguments != null)
+ type = String.Concat(type, "`", typeArguments.Count);
+
+ var returnType = new CodeTypeReference(type);
+ if (ns != null)
+ returnType.Options |= CodeTypeReferenceOptions.GlobalReference;
+
+ if (typeArguments != null)
+ {
+ foreach (var typeArg in typeArguments)
+ {
+ var ns_uri = "";
+ var _type = typeArg;
+ if (typeArg.Contains(":"))
+ {
+ var prefix = typeArg.Split(':')[0].Trim();
+ ns_uri = getNamespaceOfPrefix(prefix);
+ _type = typeArg.Split(':')[1].Trim();
+ }
+ returnType.TypeArguments.Add(GetType(ns_uri, _type, null, getNamespaceOfPrefix));
+ }
+ }
+
+ return returnType;
+ }
+
+ static string GetNamespace(string namespaceuri)
+ {
+ if (!XmlnsHelper.IsCustom(namespaceuri))
+ return "Xamarin.Forms";
+ if (namespaceuri == "http://schemas.microsoft.com/winfx/2009/xaml")
+ return "System";
+ if (namespaceuri != "http://schemas.microsoft.com/winfx/2006/xaml" && !namespaceuri.Contains("clr-namespace"))
+ throw new Exception(String.Format("Can't load types from xmlns {0}", namespaceuri));
+ return XmlnsHelper.ParseNamespaceFromXmlns(namespaceuri);
+ }
+ }
+} \ No newline at end of file