From 95b769b6582fe74828734e8377510118f4327602 Mon Sep 17 00:00:00 2001 From: Fang Xiaohui Date: Thu, 1 Jul 2021 13:45:04 +0800 Subject: [PATCH] [NUI][EXaml] Support Array in EXaml --- .../internal/EXaml/Action/CallAddMethodAction.cs | 6 +- .../src/internal/EXaml/Action/OtherActions.cs | 86 ++++++++++++++++++ .../internal/EXaml/Block/GatherAssembliesBlock.cs | 2 +- .../EXaml/Block/GatherBindablePropertiesBlock.cs | 2 +- .../src/internal/EXaml/Block/GatherEventsBlock.cs | 2 +- .../src/internal/EXaml/Block/GatherMethodsBlock.cs | 2 +- .../internal/EXaml/Block/GatherPropertiesBlock.cs | 2 +- .../src/internal/EXaml/Block/GatherTypesBlock.cs | 2 +- src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs | 5 ++ src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs | 41 ++++++++- .../src/internal/EXaml/Operation/CallAddMethod.cs | 12 ++- .../EXaml/Operation/CreateArrayInstance.cs | 59 ++++++++++++ .../EXaml/Operation/SetBindalbeProperty.cs | 12 ++- .../src/internal/EXaml/Operation/SetProperty.cs | 18 ++-- .../src/internal/Xaml/CreateValuesVisitor.cs | 12 +++ src/Tizen.NUI/src/internal/Xaml/XamlParser.cs | 100 +++++++++++++++++++++ src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs | 37 ++++++++ 17 files changed, 366 insertions(+), 34 deletions(-) create mode 100755 src/Tizen.NUI/src/internal/EXaml/Action/OtherActions.cs create mode 100755 src/Tizen.NUI/src/internal/EXaml/Operation/CreateArrayInstance.cs diff --git a/src/Tizen.NUI/src/internal/EXaml/Action/CallAddMethodAction.cs b/src/Tizen.NUI/src/internal/EXaml/Action/CallAddMethodAction.cs index fc69b14..259abce 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Action/CallAddMethodAction.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Action/CallAddMethodAction.cs @@ -64,13 +64,13 @@ namespace Tizen.NUI.EXaml public void OnActive() { - if (null != childOp && childOp.ValueList.Count > 2 && (childOp.ValueList[0] is Instance instance) && (childOp.ValueList[1] is Instance child)) + if (null != childOp && childOp.ValueList.Count > 2 && (childOp.ValueList[0] is Instance instance)) { int parentIndex = instance.Index; - int childIndex = child.Index; + var child = childOp.ValueList[1]; int methodIndex = (int)childOp.ValueList[2]; - globalDataList.Operations.Add(new CallAddMethod(globalDataList, parentIndex, childIndex, methodIndex)); + globalDataList.Operations.Add(new CallAddMethod(globalDataList, parentIndex, child, methodIndex)); } } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Action/OtherActions.cs b/src/Tizen.NUI/src/internal/EXaml/Action/OtherActions.cs new file mode 100755 index 0000000..944e01d --- /dev/null +++ b/src/Tizen.NUI/src/internal/EXaml/Action/OtherActions.cs @@ -0,0 +1,86 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections.Generic; +using System.Text; +using Tizen.NUI.Binding; +using Tizen.NUI.Binding.Internals; + +namespace Tizen.NUI.EXaml +{ + internal class OtherActions : Action + { + internal OtherActions(GlobalDataList globalDataList, Action parent) + { + this.parent = parent; + this.globalDataList = globalDataList; + } + + private Action parent; + private GlobalDataList globalDataList; + + public Action DealChar(char c) + { + switch (c) + { + case ' ': + case '\n': + case '\r': + break; + + case 'a': + parent?.OnActive(); + return parent; + + case '(': + getValues = new GetValueListAction(')', this); + return getValues; + } + + return this; + } + + public void Init() + { + } + + public void OnActive() + { + if (null != getValues) + { + int index = (int)getValues.ValueList[0]; + + switch (index) + { + case 0: + int typeIndex = (int)getValues.ValueList[1]; + var items = getValues.ValueList[2] as List; + var createArrayInstanceOp = new CreateArrayInstance(globalDataList, typeIndex, items); + globalDataList.Operations.Add(createArrayInstanceOp); + break; + + default: + break; + } + } + + getValues = null; + } + + private GetValueListAction getValues; + } +} diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherAssembliesBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherAssembliesBlock.cs index 0f7c90d..7fb9094 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherAssembliesBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherAssembliesBlock.cs @@ -63,7 +63,7 @@ namespace Tizen.NUI.EXaml public void OnActive() { var readedAssemblyName = childOp.Value as string; - globalDataList.Operations.Add(new GatherAssembly(globalDataList, readedAssemblyName)); + globalDataList.PreLoadOperations.Add(new GatherAssembly(globalDataList, readedAssemblyName)); childOp = null; } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherBindablePropertiesBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherBindablePropertiesBlock.cs index 1c9ca08..1bd68c6 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherBindablePropertiesBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherBindablePropertiesBlock.cs @@ -65,7 +65,7 @@ namespace Tizen.NUI.EXaml int typeIndex = int.Parse(childOp.ValueList[0] as string); string propertyName = childOp.ValueList[1] as string; - globalDataList.Operations.Add(new GatherBindableProperties(globalDataList, typeIndex, propertyName)); + globalDataList.PreLoadOperations.Add(new GatherBindableProperties(globalDataList, typeIndex, propertyName)); } } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherEventsBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherEventsBlock.cs index 0caeb7b..9f6f6bf 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherEventsBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherEventsBlock.cs @@ -65,7 +65,7 @@ namespace Tizen.NUI.EXaml int typeIndex = int.Parse(childOp.ValueList[0] as string); string eventName = childOp.ValueList[1] as string; - globalDataList.Operations.Add(new GatherEvent(globalDataList, typeIndex, eventName)); + globalDataList.PreLoadOperations.Add(new GatherEvent(globalDataList, typeIndex, eventName)); } } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherMethodsBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherMethodsBlock.cs index 7fb1d8b..eb43080 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherMethodsBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherMethodsBlock.cs @@ -72,7 +72,7 @@ namespace Tizen.NUI.EXaml int typeIndex = int.Parse(childOp.ValueList[0] as string); string name = childOp.ValueList[1] as string; var paramList = childOp.ValueList[2] as List; - globalDataList.Operations.Add(new GatherMethod(globalDataList, typeIndex, name, paramList)); + globalDataList.PreLoadOperations.Add(new GatherMethod(globalDataList, typeIndex, name, paramList)); } } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherPropertiesBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherPropertiesBlock.cs index 3ec8694..3de2f28 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherPropertiesBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherPropertiesBlock.cs @@ -65,7 +65,7 @@ namespace Tizen.NUI.EXaml int typeIndex = int.Parse(childOp.ValueList[0] as string); string propertyName = childOp.ValueList[1] as string; - globalDataList.Operations.Add(new GatherProperty(globalDataList, typeIndex, propertyName)); + globalDataList.PreLoadOperations.Add(new GatherProperty(globalDataList, typeIndex, propertyName)); } } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Block/GatherTypesBlock.cs b/src/Tizen.NUI/src/internal/EXaml/Block/GatherTypesBlock.cs index 0fe0255..25a2419 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Block/GatherTypesBlock.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Block/GatherTypesBlock.cs @@ -62,7 +62,7 @@ namespace Tizen.NUI.EXaml public void OnActive() { - globalDataList.Operations.Add(GatherType(childOp.ValueList)); + globalDataList.PreLoadOperations.Add(GatherType(childOp.ValueList)); childOp = null; } diff --git a/src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs b/src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs index 1786e31..ae26f86 100755 --- a/src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs +++ b/src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs @@ -24,6 +24,11 @@ namespace Tizen.NUI.EXaml { internal class GlobalDataList { + internal List PreLoadOperations + { + get; + } = new List(); + internal List Operations { get; diff --git a/src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs b/src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs index 9ff8fe8..8e4d23d 100755 --- a/src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs +++ b/src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs @@ -22,12 +22,10 @@ namespace Tizen.NUI.EXaml { internal static class LoadEXaml { - internal static void Load(object view, string xaml) + internal static GlobalDataList GatherDataList(string xaml) { var globalDataList = new GlobalDataList(); - CreateInstanceAction.Root = view; - int index = 0; var createInstance = new CreateInstanceAction(globalDataList, null); @@ -42,6 +40,7 @@ namespace Tizen.NUI.EXaml var setDynamicResourceAction = new SetDynamicResourceAction(globalDataList, null); var addToResourceDictionaryAction = new AddToResourceDictionaryAction(globalDataList, null); var setBindingAction = new SetBindingAction(globalDataList, null); + var otherActions = new OtherActions(globalDataList, null); foreach (char c in xaml) { @@ -147,6 +146,11 @@ namespace Tizen.NUI.EXaml currentOp = setBindingAction; currentOp.Init(); break; + + case 'a': + currentOp = otherActions; + currentOp.Init(); + break; } } else @@ -155,6 +159,37 @@ namespace Tizen.NUI.EXaml } } + foreach (var op in globalDataList.PreLoadOperations) + { + op.Do(); + } + + return globalDataList; + } + + internal static void Load(object view, string xaml) + { + var globalDataList = GatherDataList(xaml); + + CreateInstanceAction.Root = view; + + foreach (var op in globalDataList.Operations) + { + op.Do(); + } + } + + internal static void Load(object view, object preloadData) + { + var globalDataList = preloadData as GlobalDataList; + + if (null == globalDataList) + { + return; + } + + CreateInstanceAction.Root = view; + foreach (var op in globalDataList.Operations) { op.Do(); diff --git a/src/Tizen.NUI/src/internal/EXaml/Operation/CallAddMethod.cs b/src/Tizen.NUI/src/internal/EXaml/Operation/CallAddMethod.cs index 0a6068b..d74ea78 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Operation/CallAddMethod.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Operation/CallAddMethod.cs @@ -26,10 +26,10 @@ namespace Tizen.NUI.EXaml { internal class CallAddMethod : Operation { - public CallAddMethod(GlobalDataList globalDataList, int parentIndex, int childIndex, int methodIndex) + public CallAddMethod(GlobalDataList globalDataList, int parentIndex, object child, int methodIndex) { this.parentIndex = parentIndex; - this.childIndex = childIndex; + this.child = child; this.methodIndex = methodIndex; this.globalDataList = globalDataList; } @@ -39,14 +39,18 @@ namespace Tizen.NUI.EXaml public void Do() { object parent = globalDataList.GatheredInstances[parentIndex]; - object child = globalDataList.GatheredInstances[childIndex]; var method = globalDataList.GatheredMethods[methodIndex]; + if (child is Instance) + { + child = globalDataList.GatheredInstances[(child as Instance).Index]; + } + method.Invoke(parent, new object[] { child }); } private int parentIndex; - private int childIndex; + private object child; private int methodIndex; } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Operation/CreateArrayInstance.cs b/src/Tizen.NUI/src/internal/EXaml/Operation/CreateArrayInstance.cs new file mode 100755 index 0000000..8c7d0e5 --- /dev/null +++ b/src/Tizen.NUI/src/internal/EXaml/Operation/CreateArrayInstance.cs @@ -0,0 +1,59 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; +using Tizen.NUI.Binding.Internals; +using Tizen.NUI.Xaml; + +namespace Tizen.NUI.EXaml +{ + internal class CreateArrayInstance : Operation + { + public CreateArrayInstance(GlobalDataList globalDataList, int typeIndex, List items) + { + this.typeIndex = typeIndex; + this.items = items; + this.globalDataList = globalDataList; + } + + private GlobalDataList globalDataList; + + public void Do() + { + var type = globalDataList.GatheredTypes[typeIndex]; + var array = Array.CreateInstance(type, items.Count); + + for (int i = 0; i < items.Count; i++) + { + if (items[i] is Instance instance) + { + ((IList)array)[i] = globalDataList.GatheredInstances[instance.Index]; + } + } + + globalDataList.GatheredInstances.Add(array); + } + + private int typeIndex; + private List items; + } +} diff --git a/src/Tizen.NUI/src/internal/EXaml/Operation/SetBindalbeProperty.cs b/src/Tizen.NUI/src/internal/EXaml/Operation/SetBindalbeProperty.cs index 0ce1ebb..66100e1 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Operation/SetBindalbeProperty.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Operation/SetBindalbeProperty.cs @@ -44,15 +44,13 @@ namespace Tizen.NUI.EXaml { var property = globalDataList.GatheredBindableProperties[bindalbePropertyIndex]; - if (value is Instance) + if (value is Instance valueInstance) { - int valueIndex = (value as Instance).Index; - instance.SetValue(property, globalDataList.GatheredInstances[valueIndex]); - } - else - { - instance.SetValue(property, value); + int valueIndex = valueInstance.Index; + value = globalDataList.GatheredInstances[valueIndex]; } + + instance.SetValue(property, value); } } diff --git a/src/Tizen.NUI/src/internal/EXaml/Operation/SetProperty.cs b/src/Tizen.NUI/src/internal/EXaml/Operation/SetProperty.cs index a157719..d5fac4b 100755 --- a/src/Tizen.NUI/src/internal/EXaml/Operation/SetProperty.cs +++ b/src/Tizen.NUI/src/internal/EXaml/Operation/SetProperty.cs @@ -48,7 +48,7 @@ namespace Tizen.NUI.EXaml if (null == property) { - throw new Exception(String.Format("Can't find property in type {0}", instance.GetType().FullName)); + throw new Exception(String.Format("Can't find property {0} in type {1}", property.Name, instance.GetType().FullName)); } if (null == property.SetMethod) @@ -56,22 +56,18 @@ namespace Tizen.NUI.EXaml throw new Exception(String.Format("Property {0} hasn't set method", property.Name)); } - if (value is Instance) + if (value is Instance valueInstance) { - int valueIndex = (value as Instance).Index; - object realValue = globalDataList.GatheredInstances[valueIndex]; + int valueIndex = valueInstance.Index; + value = globalDataList.GatheredInstances[valueIndex]; - if (null == realValue) + if (null == value) { throw new Exception(String.Format("Can't get instance of value by index {0}", valueIndex)); } - - property.SetMethod.Invoke(instance, new object[] { realValue }); - } - else - { - property.SetMethod.Invoke(instance, new object[] { value }); } + + property.SetMethod.Invoke(instance, new object[] { value }); } private int instanceIndex; diff --git a/src/Tizen.NUI/src/internal/Xaml/CreateValuesVisitor.cs b/src/Tizen.NUI/src/internal/Xaml/CreateValuesVisitor.cs index 9423cd1..ed6bf55 100755 --- a/src/Tizen.NUI/src/internal/Xaml/CreateValuesVisitor.cs +++ b/src/Tizen.NUI/src/internal/Xaml/CreateValuesVisitor.cs @@ -322,7 +322,19 @@ namespace Tizen.NUI.Xaml }; var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch); if (mi == null) + { + if (node is ElementNode elementNode) + { + var nodeTypeExtension = XamlParser.GetElementTypeExtension(node.XmlType, elementNode, Context.RootElement?.GetType().GetTypeInfo().Assembly); + mi = nodeTypeExtension?.GetRuntimeMethods().FirstOrDefault(isMatch); + } + } + + if (mi == null) + { throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})"); + } + return mi.Invoke(null, arguments); } diff --git a/src/Tizen.NUI/src/internal/Xaml/XamlParser.cs b/src/Tizen.NUI/src/internal/Xaml/XamlParser.cs index 45e0804..3f2e5e3 100755 --- a/src/Tizen.NUI/src/internal/Xaml/XamlParser.cs +++ b/src/Tizen.NUI/src/internal/Xaml/XamlParser.cs @@ -474,5 +474,105 @@ namespace Tizen.NUI.Xaml return type; } + + public static Type GetElementTypeExtension(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly) + { + if (s_xmlnsDefinitions == null) + GatherXmlnsDefinitionAttributes(currentAssembly); + + var namespaceURI = xmlType.NamespaceUri; + var elementName = xmlType.Name; + var typeArguments = xmlType.TypeArguments; + + if (elementName.Contains("-")) + { + elementName = elementName.Replace('-', '+'); + } + + var lookupAssemblies = new List(); + var lookupNames = new List(); + + foreach (var xmlnsDef in s_xmlnsDefinitions) + { + if (xmlnsDef.XmlNamespace != namespaceURI) + continue; + lookupAssemblies.Add(xmlnsDef); + } + + if (lookupAssemblies.Count == 0) + { + string ns, asmstring, _; + XmlnsHelper.ParseXmlns(namespaceURI, out _, out ns, out asmstring); + lookupAssemblies.Add(new XmlnsDefinitionAttribute(namespaceURI, ns) + { + AssemblyName = asmstring ?? currentAssembly.FullName + }); + } + + lookupNames.Add(elementName + "Extension"); + + for (var i = 0; i < lookupNames.Count; i++) + { + var name = lookupNames[i]; + if (name.Contains(":")) + name = name.Substring(name.LastIndexOf(':') + 1); + if (typeArguments != null) + name += "`" + typeArguments.Count; //this will return an open generic Type + lookupNames[i] = name; + } + + Type type = null; + foreach (var asm in lookupAssemblies) + { + foreach (var name in lookupNames) + { + if ((type = Type.GetType($"{asm.ClrNamespace}.{name}, {asm.AssemblyName}")) != null) + break; + + if ((type = currentAssembly.GetType($"{asm.ClrNamespace}.{name}")) != null) + { + break; + } + + if ('?' == name.Last()) + { + string nameOfNotNull = name.Substring(0, name.Length - 1); + Type typeofNotNull = Type.GetType($"{asm.ClrNamespace}.{nameOfNotNull}, {asm.AssemblyName}"); + + if (null != typeofNotNull) + { + type = typeof(Nullable<>).MakeGenericType(new Type[] { typeofNotNull }); + break; + } + } + } + + if (type != null) + break; + } + + if (type != null && typeArguments != null) + { + XamlParseException innerexception = null; + var args = typeArguments.Select(delegate (XmlType xmltype) + { + XamlParseException xpe; + var t = GetElementType(xmltype, xmlInfo, currentAssembly, out xpe); + if (xpe != null) + { + innerexception = xpe; + return null; + } + return t; + }).ToArray(); + if (innerexception != null) + { + return null; + } + type = type.MakeGenericType(args); + } + + return type; + } } } diff --git a/src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs b/src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs index 9d752c9..ff2df56 100755 --- a/src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs +++ b/src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs @@ -78,6 +78,43 @@ namespace Tizen.NUI.EXaml return view; } + /// Internal used, will never be opened. + [EditorBrowsable(EditorBrowsableState.Never)] + public static T LoadFromEXamlByRelativePath(this T view, string eXamlPath) + { + if (null == eXamlPath) + { + return view; + } + + MainAssembly = view.GetType().Assembly; + + string resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource; + + Tizen.Log.Fatal("NUI", "the resource path: " + resource); + int windowWidth = NUIApplication.GetDefaultWindow().Size.Width; + int windowHeight = NUIApplication.GetDefaultWindow().Size.Height; + + string likelyResourcePath = resource + eXamlPath; + + //Find the xaml file in the layout folder + if (File.Exists(likelyResourcePath)) + { + StreamReader reader = new StreamReader(likelyResourcePath); + var xaml = reader.ReadToEnd(); + reader.Close(); + reader.Dispose(); + + LoadEXaml.Load(view, xaml); + } + else + { + throw new Exception($"Can't find examl file {eXamlPath}"); + } + + return view; + } + /// Used for TCT and TC coverage, will never be opened. [EditorBrowsable(EditorBrowsableState.Never)] public static T LoadFromEXaml(this T view, string eXamlStr) -- 2.7.4