2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Globalization;
22 using System.Reflection;
24 using Tizen.NUI.Binding.Internals;
25 using Tizen.NUI.Binding;
28 namespace Tizen.NUI.Xaml
30 internal class CreateValuesVisitor : IXamlNodeVisitor
32 public CreateValuesVisitor(HydrationContext context)
37 Dictionary<INode, object> Values
39 get { return Context.Values; }
42 HydrationContext Context { get; }
44 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
45 public bool StopOnDataTemplate => true;
46 public bool StopOnResourceDictionary => false;
47 public bool VisitNodeOnDataTemplate => false;
48 public bool SkipChildren(INode node, INode parentNode) => false;
49 public bool IsResourceDictionary(ElementNode node) => typeof(ResourceDictionary).IsAssignableFrom(Context.Types[node]);
51 public void Visit(ValueNode node, INode parentNode)
53 Values[node] = node.Value;
56 public void Visit(MarkupNode node, INode parentNode)
60 public void Visit(ElementNode node, INode parentNode)
64 XamlParseException xpe;
65 var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().GetTypeInfo().Assembly,
68 throw new ArgumentNullException(null, "type should not be null");
72 Context.Types[node] = type;
74 if (IsXaml2009LanguagePrimitive(node))
75 value = CreateLanguagePrimitive(type, node);
76 else if (node.Properties.ContainsKey(XmlName.xArguments) || node.Properties.ContainsKey(XmlName.xFactoryMethod))
77 value = CreateFromFactory(type, node);
80 .DeclaredConstructors.Any(
82 ci.IsPublic && ci.GetParameters().Length != 0 &&
83 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute)))) &&
84 ValidateCtorArguments(type, node, out ctorargname))
85 value = CreateFromParameterizedConstructor(type, node);
86 else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) &&
87 !ValidateCtorArguments(type, node, out ctorargname))
89 throw new XamlParseException($"The Property {ctorargname} is required to create a {type?.FullName} object.", node);
93 //this is a trick as the DataTemplate parameterless ctor is internal, and we can't CreateInstance(..., false) on WP7
96 if (type == typeof(DataTemplate))
97 value = new DataTemplate();
98 if (type == typeof(ControlTemplate))
99 value = new ControlTemplate();
100 if (value == null && node.CollectionItems.Any() && node.CollectionItems.First() is ValueNode)
102 var serviceProvider = new XamlServiceProvider(node, Context);
103 var converted = ((ValueNode)node.CollectionItems.First()).Value.ConvertTo(type, () => type.GetTypeInfo(),
105 if (converted != null && converted.GetType() == type)
110 if (type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0))
112 //default constructor
113 value = Activator.CreateInstance(type);
117 ConstructorInfo constructorInfo = null;
119 //constructor with all default parameters
120 foreach (var constructor in type.GetConstructors())
122 if (!constructor.IsStatic)
124 bool areAllParamsDefault = true;
126 foreach (var param in constructor.GetParameters())
128 if (!param.HasDefaultValue)
130 areAllParamsDefault = false;
135 if (areAllParamsDefault)
137 if (null == constructorInfo)
139 constructorInfo = constructor;
143 throw new XamlParseException($"{type.FullName} has more than one constructor which params are all default.", node);
149 if (null == constructorInfo)
151 throw new XamlParseException($"{type.FullName} has no constructor which params are all default.", node);
154 List<object> defaultParams = new List<object>();
155 foreach (var param in constructorInfo.GetParameters())
157 defaultParams.Add(param.DefaultValue);
160 value = Activator.CreateInstance(type, defaultParams.ToArray());
162 if (value is Element element)
164 if (null != Application.Current)
166 Application.Current.XamlResourceChanged += element.OnResourcesChanged;
169 element.IsCreateByXaml = true;
170 element.LineNumber = node.LineNumber;
171 element.LinePosition = node.LinePosition;
175 catch (TargetInvocationException e)
177 if (e.InnerException is XamlParseException || e.InnerException is XmlException)
178 throw e.InnerException;
183 Values[node] = value;
185 var markup = value as IMarkupExtension;
186 if (markup != null && (value is TypeExtension || value is StaticExtension || value is ArrayExtension))
188 var serviceProvider = new XamlServiceProvider(node, Context);
190 var visitor = new ApplyPropertiesVisitor(Context);
191 foreach (var cnode in node.Properties.Values.ToList())
192 cnode.Accept(visitor, node);
193 foreach (var cnode in node.CollectionItems)
194 cnode.Accept(visitor, node);
196 value = markup.ProvideValue(serviceProvider);
199 if (!node.Properties.TryGetValue(XmlName.xKey, out xKey))
202 node.Properties.Clear();
203 node.CollectionItems.Clear();
206 node.Properties.Add(XmlName.xKey, xKey);
208 Values[node] = value;
211 if (value is BindableObject)
212 NameScope.SetNameScope(value as BindableObject, node.Namescope);
215 public void Visit(RootNode node, INode parentNode)
217 var rnode = (XamlLoader.RuntimeRootNode)node;
218 Values[node] = rnode.Root;
219 Context.Types[node] = rnode.Root.GetType();
220 var bindableRoot = rnode.Root as BindableObject;
221 if (bindableRoot != null)
222 NameScope.SetNameScope(bindableRoot, node.Namescope);
225 public void Visit(ListNode node, INode parentNode)
227 //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
229 if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
233 bool ValidateCtorArguments(Type nodeType, IElementNode node, out string missingArgName)
235 missingArgName = null;
237 nodeType.GetTypeInfo()
238 .DeclaredConstructors.FirstOrDefault(
240 ci.GetParameters().Length != 0 && ci.IsPublic &&
241 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute))));
242 if (ctorInfo == null)
244 foreach (var parameter in ctorInfo.GetParameters())
246 // Modify the namespace
248 parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")?
249 .ConstructorArguments.First()
251 if (!node.Properties.ContainsKey(new XmlName("", propname)))
253 missingArgName = propname;
261 public object CreateFromParameterizedConstructor(Type nodeType, IElementNode node)
264 nodeType.GetTypeInfo()
265 .DeclaredConstructors.FirstOrDefault(
267 ci.GetParameters().Length != 0 && ci.IsPublic &&
268 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute))));
269 object[] arguments = CreateArgumentsArray(node, ctorInfo);
271 if (arguments != null)
273 return ctorInfo?.Invoke(arguments);
281 public object CreateFromFactory(Type nodeType, IElementNode node)
283 object[] arguments = CreateArgumentsArray(node);
285 if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
288 object ret = Activator.CreateInstance(nodeType, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance | BindingFlags.OptionalParamBinding, null, arguments, CultureInfo.CurrentCulture);
289 if (ret is Element element)
291 if (null != Application.Current)
293 Application.Current.XamlResourceChanged += element.OnResourcesChanged;
296 element.IsCreateByXaml = true;
297 element.LineNumber = (node as ElementNode)?.LineNumber ?? -1;
298 element.LinePosition = (node as ElementNode)?.LinePosition ?? -1;
303 var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
304 Type[] types = arguments == null ? System.Array.Empty<Type>() : arguments.Select(a => a.GetType()).ToArray();
305 Func<MethodInfo, bool> isMatch = m =>
307 if (m.Name != factoryMethod)
309 var p = m.GetParameters();
310 if (p.Length != types.Length)
314 for (var i = 0; i < p.Length; i++)
316 if ((p[i].ParameterType.IsAssignableFrom(types[i])))
318 var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
319 ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
323 arguments[i] = op_impl.Invoke(null, new[] { arguments[i] });
327 var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
330 if (node is ElementNode elementNode)
332 var nodeTypeExtension = XamlParser.GetElementTypeExtension(node.XmlType, elementNode, Context.RootElement?.GetType().GetTypeInfo().Assembly);
333 mi = nodeTypeExtension?.GetRuntimeMethods().FirstOrDefault(isMatch);
339 throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})");
342 return mi.Invoke(null, arguments);
345 public object[] CreateArgumentsArray(IElementNode enode)
347 if (!enode.Properties.ContainsKey(XmlName.xArguments))
349 var node = enode.Properties[XmlName.xArguments];
350 var elementNode = node as ElementNode;
351 if (elementNode != null)
353 var array = new object[1];
354 array[0] = Values[elementNode];
356 if (array[0].GetType().IsClass)
358 elementNode.Accept(new ApplyPropertiesVisitor(Context, true), null);
364 var listnode = node as ListNode;
365 if (listnode != null)
367 var array = new object[listnode.CollectionItems.Count];
368 for (var i = 0; i < listnode.CollectionItems.Count; i++)
369 array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
375 public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo)
377 if (ctorInfo != null)
379 var n = ctorInfo.GetParameters().Length;
380 var array = new object[n];
381 for (var i = 0; i < n; i++)
383 var parameter = ctorInfo.GetParameters()[i];
385 parameter?.CustomAttributes?.First(attr => attr.AttributeType == typeof(ParameterAttribute))?
386 .ConstructorArguments.First()
388 var name = new XmlName("", propname);
390 if (!enode.Properties.TryGetValue(name, out node))
393 if (propname != null)
395 msg = String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName);
399 msg = "propname is null.";
401 throw new XamlParseException(msg, enode as IXmlLineInfo);
403 if (!enode.SkipProperties.Contains(name))
404 enode.SkipProperties.Add(name);
405 var value = Context.Values[node];
406 var serviceProvider = new XamlServiceProvider(enode, Context);
407 var convertedValue = value?.ConvertTo(parameter?.ParameterType, () => parameter, serviceProvider);
408 array[i] = convertedValue;
416 static bool IsXaml2009LanguagePrimitive(IElementNode node)
418 return node.NamespaceURI == XamlParser.X2009Uri;
421 static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
424 if (nodeType == typeof(string))
425 value = String.Empty;
426 else if (nodeType == typeof(Uri))
430 value = Activator.CreateInstance(nodeType);
431 if (value is Element element)
433 if (null != Application.Current)
435 Application.Current.XamlResourceChanged += element.OnResourcesChanged;
438 element.IsCreateByXaml = true;
439 element.LineNumber = (node as ElementNode)?.LineNumber ?? -1;
440 element.LinePosition = (node as ElementNode)?.LinePosition ?? -1;
444 if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
445 ((ValueNode)node.CollectionItems[0]).Value is string)
447 var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
449 if (nodeType == typeof(SByte))
452 if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
455 if (nodeType == typeof(Int16))
457 return Convert.ToInt16(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
459 if (nodeType == typeof(Int32))
461 return Convert.ToInt32(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
463 if (nodeType == typeof(Int64))
465 return Convert.ToInt64(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
467 if (nodeType == typeof(Byte))
470 if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
473 if (nodeType == typeof(UInt16))
475 return Convert.ToUInt16(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
477 if (nodeType == typeof(UInt32))
479 return Convert.ToUInt32(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
481 if (nodeType == typeof(UInt64))
483 return Convert.ToUInt64(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
485 if (nodeType == typeof(Single))
487 return GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring);
489 if (nodeType == typeof(Double))
491 return Convert.ToDouble(GraphicsTypeManager.Instance.ConvertScriptToPixel(valuestring));
493 if (nodeType == typeof(Boolean))
496 if (bool.TryParse(valuestring, out outbool))
499 if (nodeType == typeof(TimeSpan))
502 if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
505 if (nodeType == typeof(char))
508 if (char.TryParse(valuestring, out retval))
511 if (nodeType == typeof(string))
513 if (nodeType == typeof(decimal))
516 if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
520 else if (nodeType == typeof(Uri))
523 if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))