maui-linux/Xamarin.Forms.Build.Tasks/SetResourcesVisitor.cs

153 строки
5.8 KiB
C#

using System;
using System.Collections;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Xamarin.Forms.Xaml;
namespace Xamarin.Forms.Build.Tasks
{
class SetResourcesVisitor : IXamlNodeVisitor
{
public SetResourcesVisitor(ILContext context)
{
Context = context;
Module = context.Body.Method.Module;
}
public ILContext Context { get; }
ModuleDefinition Module { get; }
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
public bool StopOnDataTemplate => true;
public bool StopOnResourceDictionary => false;
public bool VisitNodeOnDataTemplate => false;
public void Visit(ValueNode node, INode parentNode)
{
XmlName propertyName;
if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName)) {
if (!IsCollectionItem(node, parentNode))
return;
string contentProperty;
if (!Context.Variables.ContainsKey((IElementNode)parentNode))
return;
var parentVar = Context.Variables[(IElementNode)parentNode];
if ((contentProperty = SetPropertiesVisitor.GetContentProperty(parentVar.VariableType)) != null)
propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty);
else
return;
}
if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" &&
propertyName.LocalName == "Ignorable")
return;
if (propertyName.LocalName != "MergedWith")
return;
Context.IL.Append(SetPropertiesVisitor.SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
}
public void Visit(MarkupNode node, INode parentNode)
{
}
public void Visit(ElementNode node, INode parentNode)
{
//Set Resources in ResourcesDictionaries
if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
{
// Collection element, implicit content, or implicit collection element.
var parentVar = Context.Variables[(IElementNode)parentNode];
if (parentVar.VariableType.ImplementsInterface(Module.ImportReference(typeof (IEnumerable))))
{
if ((parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary" ||
parentVar.VariableType.Resolve().BaseType.FullName == "Xamarin.Forms.ResourceDictionary") &&
!node.Properties.ContainsKey(XmlName.xKey))
{
node.Accept(new SetPropertiesVisitor(Context), parentNode);
if (node.XmlType.Name != "Style")
throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", node);
//if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
var vardef = Context.Variables[node];
var vardefref = new VariableDefinitionReference(vardef);
Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
if (vardef != vardefref.VariableDefinition)
{
vardef = vardefref.VariableDefinition;
Context.Body.Variables.Add(vardef);
Context.Variables[node] = vardef;
}
Context.IL.Emit(OpCodes.Ldloc, parentVar);
Context.IL.Emit(OpCodes.Ldloc, Context.Variables[node]);
Context.IL.Emit(OpCodes.Callvirt,
Module.ImportReference(
Module.ImportReference(typeof (ResourceDictionary))
.Resolve()
.Methods.Single(md => md.Name == "Add" && md.Parameters.Count == 1)));
}
else if ((parentVar.VariableType.FullName == "Xamarin.Forms.ResourceDictionary" ||
parentVar.VariableType.Resolve().BaseType.FullName == "Xamarin.Forms.ResourceDictionary") &&
node.Properties.ContainsKey(XmlName.xKey))
{
node.Accept(new SetPropertiesVisitor(Context), parentNode);
//if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
var vardef = Context.Variables[node];
var vardefref = new VariableDefinitionReference(vardef);
Context.IL.Append(SetPropertiesVisitor.ProvideValue(vardefref, Context, Module, node));
if (vardef != vardefref.VariableDefinition)
{
vardef = vardefref.VariableDefinition;
Context.Body.Variables.Add(vardef);
Context.Variables[node] = vardef;
}
// IL_0013: ldloc.0
// IL_0014: ldstr "key"
// IL_0019: ldstr "foo"
// IL_001e: callvirt instance void class [Xamarin.Forms.Core]Xamarin.Forms.ResourceDictionary::Add(string, object)
Context.IL.Emit(OpCodes.Ldloc, parentVar);
Context.IL.Emit(OpCodes.Ldstr, (node.Properties[XmlName.xKey] as ValueNode).Value as string);
var varDef = Context.Variables[node];
Context.IL.Emit(OpCodes.Ldloc, varDef);
if (varDef.VariableType.IsValueType)
Context.IL.Emit(OpCodes.Box, Module.ImportReference(varDef.VariableType));
Context.IL.Emit(OpCodes.Callvirt,
Module.ImportReference(
Module.ImportReference(typeof (ResourceDictionary))
.Resolve()
.Methods.Single(md => md.Name == "Add" && md.Parameters.Count == 2)));
}
}
}
//Set ResourcesDictionaries to their parents
XmlName propertyName;
if (SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName) &&
(propertyName.LocalName == "Resources" || propertyName.LocalName.EndsWith(".Resources", StringComparison.Ordinal)) &&
(Context.Variables[node].VariableType.FullName == "Xamarin.Forms.ResourceDictionary" ||
Context.Variables[node].VariableType.Resolve().BaseType.FullName == "Xamarin.Forms.ResourceDictionary"))
Context.IL.Append(SetPropertiesVisitor.SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
}
public void Visit(RootNode node, INode parentNode)
{
}
public void Visit(ListNode node, INode parentNode)
{
}
static bool IsCollectionItem(INode node, INode parentNode)
{
var parentList = parentNode as IListNode;
if (parentList == null)
return false;
return parentList.CollectionItems.Contains(node);
}
}
}