2 using System.Collections.Generic;
3 using System.Globalization;
5 using System.Reflection;
7 using Tizen.NUI.Binding.Internals;
8 using Tizen.NUI.Binding;
11 namespace Tizen.NUI.Xaml
13 internal class CreateValuesVisitor : IXamlNodeVisitor
15 public CreateValuesVisitor(HydrationContext context)
20 Dictionary<INode, object> Values
22 get { return Context.Values; }
25 HydrationContext Context { get; }
27 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
28 public bool StopOnDataTemplate => true;
29 public bool StopOnResourceDictionary => false;
30 public bool VisitNodeOnDataTemplate => false;
31 public bool SkipChildren(INode node, INode parentNode) => false;
32 public bool IsResourceDictionary(ElementNode node) => typeof(ResourceDictionary).IsAssignableFrom(Context.Types[node]);
34 public void Visit(ValueNode node, INode parentNode)
36 Values[node] = node.Value;
39 public void Visit(MarkupNode node, INode parentNode)
43 public void Visit(ElementNode node, INode parentNode)
47 XamlParseException xpe;
48 var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().GetTypeInfo().Assembly,
51 throw new ArgumentNullException(null, "type should not be null");
55 Context.Types[node] = type;
57 if (IsXaml2009LanguagePrimitive(node))
58 value = CreateLanguagePrimitive(type, node);
59 else if (node.Properties.ContainsKey(XmlName.xArguments) || node.Properties.ContainsKey(XmlName.xFactoryMethod))
60 value = CreateFromFactory(type, node);
63 .DeclaredConstructors.Any(
65 ci.IsPublic && ci.GetParameters().Length != 0 &&
66 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute)))) &&
67 ValidateCtorArguments(type, node, out ctorargname))
68 value = CreateFromParameterizedConstructor(type, node);
69 else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) &&
70 !ValidateCtorArguments(type, node, out ctorargname))
72 throw new XamlParseException($"The Property {ctorargname} is required to create a {type?.FullName} object.", node);
76 //this is a trick as the DataTemplate parameterless ctor is internal, and we can't CreateInstance(..., false) on WP7
79 if (type == typeof(DataTemplate))
80 value = new DataTemplate();
81 if (type == typeof(ControlTemplate))
82 value = new ControlTemplate();
83 if (value == null && node.CollectionItems.Any() && node.CollectionItems.First() is ValueNode)
85 var serviceProvider = new XamlServiceProvider(node, Context);
86 var converted = ((ValueNode)node.CollectionItems.First()).Value.ConvertTo(type, () => type.GetTypeInfo(),
88 if (converted != null && converted.GetType() == type)
93 if (type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0))
96 value = Activator.CreateInstance(type);
100 //constructor with all default parameters
101 value = Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance | BindingFlags.OptionalParamBinding, null, new object[] { Type.Missing }, CultureInfo.CurrentCulture);
103 if (value is Element)
105 if (null != Application.Current)
107 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
110 if (value is BindableObject)
112 ((BindableObject)value).IsCreateByXaml = true;
117 catch (TargetInvocationException e)
119 if (e.InnerException is XamlParseException || e.InnerException is XmlException)
120 throw e.InnerException;
125 Values[node] = value;
127 var markup = value as IMarkupExtension;
128 if (markup != null && (value is TypeExtension || value is StaticExtension || value is ArrayExtension))
130 var serviceProvider = new XamlServiceProvider(node, Context);
132 var visitor = new ApplyPropertiesVisitor(Context);
133 foreach (var cnode in node.Properties.Values.ToList())
134 cnode.Accept(visitor, node);
135 foreach (var cnode in node.CollectionItems)
136 cnode.Accept(visitor, node);
138 value = markup.ProvideValue(serviceProvider);
141 if (!node.Properties.TryGetValue(XmlName.xKey, out xKey))
144 node.Properties.Clear();
145 node.CollectionItems.Clear();
148 node.Properties.Add(XmlName.xKey, xKey);
150 Values[node] = value;
153 if (value is BindableObject)
154 NameScope.SetNameScope(value as BindableObject, node.Namescope);
157 public void Visit(RootNode node, INode parentNode)
159 var rnode = (XamlLoader.RuntimeRootNode)node;
160 Values[node] = rnode.Root;
161 Context.Types[node] = rnode.Root.GetType();
162 var bindableRoot = rnode.Root as BindableObject;
163 if (bindableRoot != null)
164 NameScope.SetNameScope(bindableRoot, node.Namescope);
167 public void Visit(ListNode node, INode parentNode)
169 //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
171 if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
175 bool ValidateCtorArguments(Type nodeType, IElementNode node, out string missingArgName)
177 missingArgName = null;
179 nodeType.GetTypeInfo()
180 .DeclaredConstructors.FirstOrDefault(
182 ci.GetParameters().Length != 0 && ci.IsPublic &&
183 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute))));
184 if (ctorInfo == null)
186 foreach (var parameter in ctorInfo.GetParameters())
188 // Modify the namespace
190 parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")?
191 .ConstructorArguments.First()
193 if (!node.Properties.ContainsKey(new XmlName("", propname)))
195 missingArgName = propname;
203 public object CreateFromParameterizedConstructor(Type nodeType, IElementNode node)
206 nodeType.GetTypeInfo()
207 .DeclaredConstructors.FirstOrDefault(
209 ci.GetParameters().Length != 0 && ci.IsPublic &&
210 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParameterAttribute))));
211 object[] arguments = CreateArgumentsArray(node, ctorInfo);
213 if (arguments != null)
215 return ctorInfo?.Invoke(arguments);
223 public object CreateFromFactory(Type nodeType, IElementNode node)
225 object[] arguments = CreateArgumentsArray(node);
227 if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
230 object ret = Activator.CreateInstance(nodeType, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance | BindingFlags.OptionalParamBinding, null, arguments, CultureInfo.CurrentCulture);
233 if (null != Application.Current)
235 Application.AddResourceChangedCallback(ret, (ret as Element).OnResourcesChanged);
238 if (ret is BindableObject)
240 ((BindableObject)ret).IsCreateByXaml = true;
246 var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
247 Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray();
248 Func<MethodInfo, bool> isMatch = m =>
250 if (m.Name != factoryMethod)
252 var p = m.GetParameters();
253 if (p.Length != types.Length)
257 for (var i = 0; i < p.Length; i++)
259 if ((p[i].ParameterType.IsAssignableFrom(types[i])))
261 var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
262 ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
266 arguments[i] = op_impl.Invoke(null, new[] { arguments[i] });
270 var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
272 throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})");
273 return mi.Invoke(null, arguments);
276 public object[] CreateArgumentsArray(IElementNode enode)
278 if (!enode.Properties.ContainsKey(XmlName.xArguments))
280 var node = enode.Properties[XmlName.xArguments];
281 var elementNode = node as ElementNode;
282 if (elementNode != null)
284 var array = new object[1];
285 array[0] = Values[elementNode];
287 if (array[0].GetType().IsClass)
289 elementNode.Accept(new ApplyPropertiesVisitor(Context, true), null);
295 var listnode = node as ListNode;
296 if (listnode != null)
298 var array = new object[listnode.CollectionItems.Count];
299 for (var i = 0; i < listnode.CollectionItems.Count; i++)
300 array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
306 public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo)
308 if (ctorInfo != null)
310 var n = ctorInfo.GetParameters().Length;
311 var array = new object[n];
312 for (var i = 0; i < n; i++)
314 var parameter = ctorInfo.GetParameters()[i];
316 parameter?.CustomAttributes?.First(attr => attr.AttributeType == typeof(ParameterAttribute))?
317 .ConstructorArguments.First()
319 var name = new XmlName("", propname);
321 if (!enode.Properties.TryGetValue(name, out node))
324 if (propname != null)
326 msg = String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName);
330 msg = "propname is null.";
332 throw new XamlParseException(msg, enode as IXmlLineInfo);
334 if (!enode.SkipProperties.Contains(name))
335 enode.SkipProperties.Add(name);
336 var value = Context.Values[node];
337 var serviceProvider = new XamlServiceProvider(enode, Context);
338 var convertedValue = value?.ConvertTo(parameter?.ParameterType, () => parameter, serviceProvider);
339 array[i] = convertedValue;
347 static bool IsXaml2009LanguagePrimitive(IElementNode node)
349 return node.NamespaceURI == XamlParser.X2009Uri;
352 static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
355 if (nodeType == typeof(string))
356 value = String.Empty;
357 else if (nodeType == typeof(Uri))
361 value = Activator.CreateInstance(nodeType);
362 if (value is Element)
364 if (null != Application.Current)
366 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
369 if (value is BindableObject)
371 ((BindableObject)value).IsCreateByXaml = true;
376 if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
377 ((ValueNode)node.CollectionItems[0]).Value is string)
379 var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
381 if (nodeType == typeof(SByte))
384 if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
387 if (nodeType == typeof(Int16))
390 if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
393 if (nodeType == typeof(Int32))
396 if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
399 if (nodeType == typeof(Int64))
402 if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
405 if (nodeType == typeof(Byte))
408 if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
411 if (nodeType == typeof(UInt16))
414 if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
417 if (nodeType == typeof(UInt32))
420 if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
423 if (nodeType == typeof(UInt64))
426 if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
429 if (nodeType == typeof(Single))
432 if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
435 if (nodeType == typeof(Double))
438 if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
441 if (nodeType == typeof(Boolean))
444 if (bool.TryParse(valuestring, out outbool))
447 if (nodeType == typeof(TimeSpan))
450 if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
453 if (nodeType == typeof(char))
456 if (char.TryParse(valuestring, out retval))
459 if (nodeType == typeof(string))
461 if (nodeType == typeof(decimal))
464 if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
468 else if (nodeType == typeof(Uri))
471 if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))