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;
33 public void Visit(ValueNode node, INode parentNode)
35 Values[node] = node.Value;
38 public void Visit(MarkupNode node, INode parentNode)
42 public void Visit(ElementNode node, INode parentNode)
46 XamlParseException xpe;
47 var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().GetTypeInfo().Assembly,
52 Context.Types[node] = type;
54 if (IsXaml2009LanguagePrimitive(node))
55 value = CreateLanguagePrimitive(type, node);
56 else if (node.Properties.ContainsKey(XmlName.xArguments) || node.Properties.ContainsKey(XmlName.xFactoryMethod))
57 value = CreateFromFactory(type, node);
60 .DeclaredConstructors.Any(
62 ci.IsPublic && ci.GetParameters().Length != 0 &&
63 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute)))) &&
64 ValidateCtorArguments(type, node, out ctorargname))
65 value = CreateFromParameterizedConstructor(type, node);
66 else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) &&
67 !ValidateCtorArguments(type, node, out ctorargname))
69 throw new XamlParseException($"The Property {ctorargname} is required to create a {type?.FullName} object.", node);
73 //this is a trick as the DataTemplate parameterless ctor is internal, and we can't CreateInstance(..., false) on WP7
76 if (type == typeof (DataTemplate))
77 value = new DataTemplate();
78 if (type == typeof (ControlTemplate))
79 value = new ControlTemplate();
80 if (value == null && node.CollectionItems.Any() && node.CollectionItems.First() is ValueNode)
82 var serviceProvider = new XamlServiceProvider(node, Context);
83 var converted = ((ValueNode)node.CollectionItems.First()).Value.ConvertTo(type, () => type.GetTypeInfo(),
85 if (converted != null && converted.GetType() == type)
90 value = Activator.CreateInstance(type);
93 if (null != Application.Current)
95 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
98 if (value is BindableObject)
100 ((BindableObject)value).IsCreateByXaml = true;
105 catch (TargetInvocationException e)
107 if (e.InnerException is XamlParseException || e.InnerException is XmlException)
108 throw e.InnerException;
113 Values[node] = value;
115 var markup = value as IMarkupExtension;
116 if (markup != null && (value is TypeExtension || value is StaticExtension || value is ArrayExtension))
118 var serviceProvider = new XamlServiceProvider(node, Context);
120 var visitor = new ApplyPropertiesVisitor(Context);
121 foreach (var cnode in node.Properties.Values.ToList())
122 cnode.Accept(visitor, node);
123 foreach (var cnode in node.CollectionItems)
124 cnode.Accept(visitor, node);
126 value = markup.ProvideValue(serviceProvider);
129 if (!node.Properties.TryGetValue(XmlName.xKey, out xKey))
132 node.Properties.Clear();
133 node.CollectionItems.Clear();
136 node.Properties.Add(XmlName.xKey, xKey);
138 Values[node] = value;
141 if (value is BindableObject)
142 NameScope.SetNameScope(value as BindableObject, node.Namescope);
145 public void Visit(RootNode node, INode parentNode)
147 var rnode = (XamlLoader.RuntimeRootNode)node;
148 Values[node] = rnode.Root;
149 Context.Types[node] = rnode.Root.GetType();
150 var bindableRoot = rnode.Root as BindableObject;
151 if (bindableRoot != null)
152 NameScope.SetNameScope(bindableRoot, node.Namescope);
155 public void Visit(ListNode node, INode parentNode)
157 //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
159 if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
163 bool ValidateCtorArguments(Type nodeType, IElementNode node, out string missingArgName)
165 missingArgName = null;
167 nodeType.GetTypeInfo()
168 .DeclaredConstructors.FirstOrDefault(
170 ci.GetParameters().Length != 0 && ci.IsPublic &&
171 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
172 if (ctorInfo == null)
174 foreach (var parameter in ctorInfo.GetParameters())
176 // Modify the namespace
178 parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")?
179 .ConstructorArguments.First()
181 if (!node.Properties.ContainsKey(new XmlName("", propname)))
183 missingArgName = propname;
191 public object CreateFromParameterizedConstructor(Type nodeType, IElementNode node)
194 nodeType.GetTypeInfo()
195 .DeclaredConstructors.FirstOrDefault(
197 ci.GetParameters().Length != 0 && ci.IsPublic &&
198 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
199 object[] arguments = CreateArgumentsArray(node, ctorInfo);
201 if (arguments != null)
203 return ctorInfo?.Invoke(arguments);
211 public object CreateFromFactory(Type nodeType, IElementNode node)
213 object[] arguments = CreateArgumentsArray(node);
215 if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
218 object ret = Activator.CreateInstance(nodeType, arguments);
221 if (null != Application.Current)
223 Application.AddResourceChangedCallback(ret, (ret as Element).OnResourcesChanged);
226 if (ret is BindableObject)
228 ((BindableObject)ret).IsCreateByXaml = true;
234 var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
235 Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray();
236 Func<MethodInfo, bool> isMatch = m => {
237 if (m.Name != factoryMethod)
239 var p = m.GetParameters();
240 if (p.Length != types.Length)
244 for (var i = 0; i < p.Length; i++) {
245 if ((p [i].ParameterType.IsAssignableFrom(types [i])))
247 var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
248 ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
252 arguments [i] = op_impl.Invoke(null, new [] { arguments [i]});
256 var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
258 throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})");
259 return mi.Invoke(null, arguments);
262 public object[] CreateArgumentsArray(IElementNode enode)
264 if (!enode.Properties.ContainsKey(XmlName.xArguments))
266 var node = enode.Properties[XmlName.xArguments];
267 var elementNode = node as ElementNode;
268 if (elementNode != null)
270 var array = new object[1];
271 array[0] = Values[elementNode];
275 var listnode = node as ListNode;
276 if (listnode != null)
278 var array = new object[listnode.CollectionItems.Count];
279 for (var i = 0; i < listnode.CollectionItems.Count; i++)
280 array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
286 public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo)
288 if( ctorInfo != null )
290 var n = ctorInfo.GetParameters().Length;
291 var array = new object[n];
292 for (var i = 0; i < n; i++)
294 var parameter = ctorInfo.GetParameters()[i];
296 parameter?.CustomAttributes?.First(attr => attr.AttributeType == typeof (ParameterAttribute))?
297 .ConstructorArguments.First()
299 var name = new XmlName("", propname);
301 if (!enode.Properties.TryGetValue(name, out node))
304 if (propname != null)
306 msg = String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName);
310 msg = "propname is null.";
312 throw new XamlParseException(msg, enode as IXmlLineInfo);
314 if (!enode.SkipProperties.Contains(name))
315 enode.SkipProperties.Add(name);
316 var value = Context.Values[node];
317 var serviceProvider = new XamlServiceProvider(enode, Context);
318 var convertedValue = value?.ConvertTo(parameter.ParameterType, () => parameter, serviceProvider);
319 array[i] = convertedValue;
327 static bool IsXaml2009LanguagePrimitive(IElementNode node)
329 return node.NamespaceURI == XamlParser.X2009Uri;
332 static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
335 if (nodeType == typeof(string))
336 value = String.Empty;
337 else if (nodeType == typeof(Uri))
341 value = Activator.CreateInstance(nodeType);
342 if (value is Element)
344 if (null != Application.Current)
346 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
349 if (value is BindableObject)
351 ((BindableObject)value).IsCreateByXaml = true;
356 if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
357 ((ValueNode)node.CollectionItems[0]).Value is string)
359 var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
361 if (nodeType == typeof(SByte)) {
363 if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
366 if (nodeType == typeof(Int16)) {
368 if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
371 if (nodeType == typeof(Int32)) {
373 if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
376 if (nodeType == typeof(Int64)) {
378 if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
381 if (nodeType == typeof(Byte)) {
383 if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
386 if (nodeType == typeof(UInt16)) {
388 if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
391 if (nodeType == typeof(UInt32)) {
393 if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
396 if (nodeType == typeof(UInt64)) {
398 if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
401 if (nodeType == typeof(Single)) {
403 if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
406 if (nodeType == typeof(Double)) {
408 if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
411 if (nodeType == typeof (Boolean))
414 if (bool.TryParse(valuestring, out outbool))
417 if (nodeType == typeof(TimeSpan)) {
419 if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
422 if (nodeType == typeof (char))
425 if (char.TryParse(valuestring, out retval))
428 if (nodeType == typeof (string))
430 if (nodeType == typeof (decimal))
433 if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
437 else if (nodeType == typeof (Uri))
440 if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))