[NUI][EXaml] Support Array in EXaml
authorFang Xiaohui <xiaohui.fang@samsung.com>
Thu, 1 Jul 2021 05:45:04 +0000 (13:45 +0800)
committerSangHyeon Jade Lee <dltkdgus1764@gmail.com>
Mon, 5 Jul 2021 09:51:44 +0000 (18:51 +0900)
17 files changed:
src/Tizen.NUI/src/internal/EXaml/Action/CallAddMethodAction.cs
src/Tizen.NUI/src/internal/EXaml/Action/OtherActions.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/EXaml/Block/GatherAssembliesBlock.cs
src/Tizen.NUI/src/internal/EXaml/Block/GatherBindablePropertiesBlock.cs
src/Tizen.NUI/src/internal/EXaml/Block/GatherEventsBlock.cs
src/Tizen.NUI/src/internal/EXaml/Block/GatherMethodsBlock.cs
src/Tizen.NUI/src/internal/EXaml/Block/GatherPropertiesBlock.cs
src/Tizen.NUI/src/internal/EXaml/Block/GatherTypesBlock.cs
src/Tizen.NUI/src/internal/EXaml/GlobalDataList.cs
src/Tizen.NUI/src/internal/EXaml/LoadEXaml.cs
src/Tizen.NUI/src/internal/EXaml/Operation/CallAddMethod.cs
src/Tizen.NUI/src/internal/EXaml/Operation/CreateArrayInstance.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/EXaml/Operation/SetBindalbeProperty.cs
src/Tizen.NUI/src/internal/EXaml/Operation/SetProperty.cs
src/Tizen.NUI/src/internal/Xaml/CreateValuesVisitor.cs
src/Tizen.NUI/src/internal/Xaml/XamlParser.cs
src/Tizen.NUI/src/public/EXaml/EXamlExtensions.cs

index fc69b14..259abce 100755 (executable)
@@ -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 (executable)
index 0000000..944e01d
--- /dev/null
@@ -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<object>;
+                        var createArrayInstanceOp = new CreateArrayInstance(globalDataList, typeIndex, items);
+                        globalDataList.Operations.Add(createArrayInstanceOp);
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+
+            getValues = null;
+        }
+
+        private GetValueListAction getValues;
+    }
+}
index 0f7c90d..7fb9094 100755 (executable)
@@ -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;
         }
     }
index 1c9ca08..1bd68c6 100755 (executable)
@@ -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));
         }
     }
 }
index 0caeb7b..9f6f6bf 100755 (executable)
@@ -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));
         }
     }
 }
index 7fb1d8b..eb43080 100755 (executable)
@@ -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<object>;
-            globalDataList.Operations.Add(new GatherMethod(globalDataList, typeIndex, name, paramList));
+            globalDataList.PreLoadOperations.Add(new GatherMethod(globalDataList, typeIndex, name, paramList));
         }
     }
 }
index 3ec8694..3de2f28 100755 (executable)
@@ -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));
         }
     }
 }
index 0fe0255..25a2419 100755 (executable)
@@ -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;
         }
 
index 1786e31..ae26f86 100755 (executable)
@@ -24,6 +24,11 @@ namespace Tizen.NUI.EXaml
 {
     internal class GlobalDataList
     {
+        internal List<Operation> PreLoadOperations
+        {
+            get;
+        } = new List<Operation>();
+
         internal List<Operation> Operations
         {
             get;
index 9ff8fe8..8e4d23d 100755 (executable)
@@ -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();
index 0a6068b..d74ea78 100755 (executable)
@@ -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 (executable)
index 0000000..8c7d0e5
--- /dev/null
@@ -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<object> 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<object> items;
+    }
+}
index 0ce1ebb..66100e1 100755 (executable)
@@ -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);
             }
         }
 
index a157719..d5fac4b 100755 (executable)
@@ -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;
index 9423cd1..ed6bf55 100755 (executable)
@@ -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);
         }
 
index 45e0804..3f2e5e3 100755 (executable)
@@ -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<XmlnsDefinitionAttribute>();
+            var lookupNames = new List<string>();
+
+            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;
+        }
     }
 }
index 9d752c9..ff2df56 100755 (executable)
@@ -78,6 +78,43 @@ namespace Tizen.NUI.EXaml
             return view;
         }
 
+        /// Internal used, will never be opened.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static T LoadFromEXamlByRelativePath<T>(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<T>(this T view, string eXamlStr)