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 => {
249 if (m.Name != factoryMethod)
251 var p = m.GetParameters();
252 if (p.Length != types.Length)
256 for (var i = 0; i < p.Length; i++) {
257 if ((p [i].ParameterType.IsAssignableFrom(types [i])))
259 var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
260 ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
264 arguments [i] = op_impl.Invoke(null, new [] { arguments [i]});
268 var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
270 throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})");
271 return mi.Invoke(null, arguments);
274 public object[] CreateArgumentsArray(IElementNode enode)
276 if (!enode.Properties.ContainsKey(XmlName.xArguments))
278 var node = enode.Properties[XmlName.xArguments];
279 var elementNode = node as ElementNode;
280 if (elementNode != null)
282 var array = new object[1];
283 array[0] = Values[elementNode];
285 if (array[0].GetType().IsClass)
287 elementNode.Accept(new ApplyPropertiesVisitor(Context, true), null);
293 var listnode = node as ListNode;
294 if (listnode != null)
296 var array = new object[listnode.CollectionItems.Count];
297 for (var i = 0; i < listnode.CollectionItems.Count; i++)
298 array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
304 public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo)
306 if( ctorInfo != null )
308 var n = ctorInfo.GetParameters().Length;
309 var array = new object[n];
310 for (var i = 0; i < n; i++)
312 var parameter = ctorInfo.GetParameters()[i];
314 parameter?.CustomAttributes?.First(attr => attr.AttributeType == typeof (ParameterAttribute))?
315 .ConstructorArguments.First()
317 var name = new XmlName("", propname);
319 if (!enode.Properties.TryGetValue(name, out node))
322 if (propname != null)
324 msg = String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName);
328 msg = "propname is null.";
330 throw new XamlParseException(msg, enode as IXmlLineInfo);
332 if (!enode.SkipProperties.Contains(name))
333 enode.SkipProperties.Add(name);
334 var value = Context.Values[node];
335 var serviceProvider = new XamlServiceProvider(enode, Context);
336 var convertedValue = value?.ConvertTo(parameter?.ParameterType, () => parameter, serviceProvider);
337 array[i] = convertedValue;
345 static bool IsXaml2009LanguagePrimitive(IElementNode node)
347 return node.NamespaceURI == XamlParser.X2009Uri;
350 static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
353 if (nodeType == typeof(string))
354 value = String.Empty;
355 else if (nodeType == typeof(Uri))
359 value = Activator.CreateInstance(nodeType);
360 if (value is Element)
362 if (null != Application.Current)
364 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
367 if (value is BindableObject)
369 ((BindableObject)value).IsCreateByXaml = true;
374 if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
375 ((ValueNode)node.CollectionItems[0]).Value is string)
377 var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
379 if (nodeType == typeof(SByte)) {
381 if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
384 if (nodeType == typeof(Int16)) {
386 if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
389 if (nodeType == typeof(Int32)) {
391 if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
394 if (nodeType == typeof(Int64)) {
396 if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
399 if (nodeType == typeof(Byte)) {
401 if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
404 if (nodeType == typeof(UInt16)) {
406 if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
409 if (nodeType == typeof(UInt32)) {
411 if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
414 if (nodeType == typeof(UInt64)) {
416 if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
419 if (nodeType == typeof(Single)) {
421 if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
424 if (nodeType == typeof(Double)) {
426 if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
429 if (nodeType == typeof (Boolean))
432 if (bool.TryParse(valuestring, out outbool))
435 if (nodeType == typeof(TimeSpan)) {
437 if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
440 if (nodeType == typeof (char))
443 if (char.TryParse(valuestring, out retval))
446 if (nodeType == typeof (string))
448 if (nodeType == typeof (decimal))
451 if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
455 else if (nodeType == typeof (Uri))
458 if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))