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,
53 Context.Types[node] = type;
55 if (IsXaml2009LanguagePrimitive(node))
56 value = CreateLanguagePrimitive(type, node);
57 else if (node.Properties.ContainsKey(XmlName.xArguments) || node.Properties.ContainsKey(XmlName.xFactoryMethod))
58 value = CreateFromFactory(type, node);
61 .DeclaredConstructors.Any(
63 ci.IsPublic && ci.GetParameters().Length != 0 &&
64 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute)))) &&
65 ValidateCtorArguments(type, node, out ctorargname))
66 value = CreateFromParameterizedConstructor(type, node);
67 else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) &&
68 !ValidateCtorArguments(type, node, out ctorargname))
70 throw new XamlParseException($"The Property {ctorargname} is required to create a {type?.FullName} object.", node);
74 //this is a trick as the DataTemplate parameterless ctor is internal, and we can't CreateInstance(..., false) on WP7
77 if (type == typeof (DataTemplate))
78 value = new DataTemplate();
79 if (type == typeof (ControlTemplate))
80 value = new ControlTemplate();
81 if (value == null && node.CollectionItems.Any() && node.CollectionItems.First() is ValueNode)
83 var serviceProvider = new XamlServiceProvider(node, Context);
84 var converted = ((ValueNode)node.CollectionItems.First()).Value.ConvertTo(type, () => type.GetTypeInfo(),
86 if (converted != null && converted.GetType() == type)
91 if (type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0))
94 value = Activator.CreateInstance(type);
98 //constructor with all default parameters
99 value = Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance | BindingFlags.OptionalParamBinding, null, new object[] { Type.Missing }, CultureInfo.CurrentCulture);
101 if (value is Element)
103 if (null != Application.Current)
105 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
108 if (value is BindableObject)
110 ((BindableObject)value).IsCreateByXaml = true;
115 catch (TargetInvocationException e)
117 if (e.InnerException is XamlParseException || e.InnerException is XmlException)
118 throw e.InnerException;
123 Values[node] = value;
125 var markup = value as IMarkupExtension;
126 if (markup != null && (value is TypeExtension || value is StaticExtension || value is ArrayExtension))
128 var serviceProvider = new XamlServiceProvider(node, Context);
130 var visitor = new ApplyPropertiesVisitor(Context);
131 foreach (var cnode in node.Properties.Values.ToList())
132 cnode.Accept(visitor, node);
133 foreach (var cnode in node.CollectionItems)
134 cnode.Accept(visitor, node);
136 value = markup.ProvideValue(serviceProvider);
139 if (!node.Properties.TryGetValue(XmlName.xKey, out xKey))
142 node.Properties.Clear();
143 node.CollectionItems.Clear();
146 node.Properties.Add(XmlName.xKey, xKey);
148 Values[node] = value;
151 if (value is BindableObject)
152 NameScope.SetNameScope(value as BindableObject, node.Namescope);
155 public void Visit(RootNode node, INode parentNode)
157 var rnode = (XamlLoader.RuntimeRootNode)node;
158 Values[node] = rnode.Root;
159 Context.Types[node] = rnode.Root.GetType();
160 var bindableRoot = rnode.Root as BindableObject;
161 if (bindableRoot != null)
162 NameScope.SetNameScope(bindableRoot, node.Namescope);
165 public void Visit(ListNode node, INode parentNode)
167 //this is a gross hack to keep ListNode alive. ListNode must go in favor of Properties
169 if (ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out name))
173 bool ValidateCtorArguments(Type nodeType, IElementNode node, out string missingArgName)
175 missingArgName = null;
177 nodeType.GetTypeInfo()
178 .DeclaredConstructors.FirstOrDefault(
180 ci.GetParameters().Length != 0 && ci.IsPublic &&
181 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
182 if (ctorInfo == null)
184 foreach (var parameter in ctorInfo.GetParameters())
186 // Modify the namespace
188 parameter.CustomAttributes.First(ca => ca.AttributeType.FullName == "Tizen.NUI.Binding.ParameterAttribute")?
189 .ConstructorArguments.First()
191 if (!node.Properties.ContainsKey(new XmlName("", propname)))
193 missingArgName = propname;
201 public object CreateFromParameterizedConstructor(Type nodeType, IElementNode node)
204 nodeType.GetTypeInfo()
205 .DeclaredConstructors.FirstOrDefault(
207 ci.GetParameters().Length != 0 && ci.IsPublic &&
208 ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute))));
209 object[] arguments = CreateArgumentsArray(node, ctorInfo);
211 if (arguments != null)
213 return ctorInfo?.Invoke(arguments);
221 public object CreateFromFactory(Type nodeType, IElementNode node)
223 object[] arguments = CreateArgumentsArray(node);
225 if (!node.Properties.ContainsKey(XmlName.xFactoryMethod))
228 object ret = Activator.CreateInstance(nodeType, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance | BindingFlags.OptionalParamBinding, null, arguments, CultureInfo.CurrentCulture);
231 if (null != Application.Current)
233 Application.AddResourceChangedCallback(ret, (ret as Element).OnResourcesChanged);
236 if (ret is BindableObject)
238 ((BindableObject)ret).IsCreateByXaml = true;
244 var factoryMethod = ((string)((ValueNode)node.Properties[XmlName.xFactoryMethod]).Value);
245 Type[] types = arguments == null ? new Type[0] : arguments.Select(a => a.GetType()).ToArray();
246 Func<MethodInfo, bool> isMatch = m => {
247 if (m.Name != factoryMethod)
249 var p = m.GetParameters();
250 if (p.Length != types.Length)
254 for (var i = 0; i < p.Length; i++) {
255 if ((p [i].ParameterType.IsAssignableFrom(types [i])))
257 var op_impl = p[i].ParameterType.GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType)
258 ?? types[i].GetImplicitConversionOperator(fromType: types[i], toType: p[i].ParameterType);
262 arguments [i] = op_impl.Invoke(null, new [] { arguments [i]});
266 var mi = nodeType.GetRuntimeMethods().FirstOrDefault(isMatch);
268 throw new MissingMemberException($"No static method found for {nodeType.FullName}::{factoryMethod} ({string.Join(", ", types.Select(t => t.FullName))})");
269 return mi.Invoke(null, arguments);
272 public object[] CreateArgumentsArray(IElementNode enode)
274 if (!enode.Properties.ContainsKey(XmlName.xArguments))
276 var node = enode.Properties[XmlName.xArguments];
277 var elementNode = node as ElementNode;
278 if (elementNode != null)
280 var array = new object[1];
281 array[0] = Values[elementNode];
285 var listnode = node as ListNode;
286 if (listnode != null)
288 var array = new object[listnode.CollectionItems.Count];
289 for (var i = 0; i < listnode.CollectionItems.Count; i++)
290 array[i] = Values[(ElementNode)listnode.CollectionItems[i]];
296 public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo)
298 if( ctorInfo != null )
300 var n = ctorInfo.GetParameters().Length;
301 var array = new object[n];
302 for (var i = 0; i < n; i++)
304 var parameter = ctorInfo.GetParameters()[i];
306 parameter?.CustomAttributes?.First(attr => attr.AttributeType == typeof (ParameterAttribute))?
307 .ConstructorArguments.First()
309 var name = new XmlName("", propname);
311 if (!enode.Properties.TryGetValue(name, out node))
314 if (propname != null)
316 msg = String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName);
320 msg = "propname is null.";
322 throw new XamlParseException(msg, enode as IXmlLineInfo);
324 if (!enode.SkipProperties.Contains(name))
325 enode.SkipProperties.Add(name);
326 var value = Context.Values[node];
327 var serviceProvider = new XamlServiceProvider(enode, Context);
328 var convertedValue = value?.ConvertTo(parameter?.ParameterType, () => parameter, serviceProvider);
329 array[i] = convertedValue;
337 static bool IsXaml2009LanguagePrimitive(IElementNode node)
339 return node.NamespaceURI == XamlParser.X2009Uri;
342 static object CreateLanguagePrimitive(Type nodeType, IElementNode node)
345 if (nodeType == typeof(string))
346 value = String.Empty;
347 else if (nodeType == typeof(Uri))
351 value = Activator.CreateInstance(nodeType);
352 if (value is Element)
354 if (null != Application.Current)
356 Application.AddResourceChangedCallback(value, (value as Element).OnResourcesChanged);
359 if (value is BindableObject)
361 ((BindableObject)value).IsCreateByXaml = true;
366 if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode &&
367 ((ValueNode)node.CollectionItems[0]).Value is string)
369 var valuestring = ((ValueNode)node.CollectionItems[0]).Value as string;
371 if (nodeType == typeof(SByte)) {
373 if (sbyte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
376 if (nodeType == typeof(Int16)) {
378 if (short.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
381 if (nodeType == typeof(Int32)) {
383 if (int.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
386 if (nodeType == typeof(Int64)) {
388 if (long.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
391 if (nodeType == typeof(Byte)) {
393 if (byte.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
396 if (nodeType == typeof(UInt16)) {
398 if (ushort.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
401 if (nodeType == typeof(UInt32)) {
403 if (uint.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
406 if (nodeType == typeof(UInt64)) {
408 if (ulong.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
411 if (nodeType == typeof(Single)) {
413 if (float.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
416 if (nodeType == typeof(Double)) {
418 if (double.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
421 if (nodeType == typeof (Boolean))
424 if (bool.TryParse(valuestring, out outbool))
427 if (nodeType == typeof(TimeSpan)) {
429 if (TimeSpan.TryParse(valuestring, CultureInfo.InvariantCulture, out retval))
432 if (nodeType == typeof (char))
435 if (char.TryParse(valuestring, out retval))
438 if (nodeType == typeof (string))
440 if (nodeType == typeof (decimal))
443 if (decimal.TryParse(valuestring, NumberStyles.Number, CultureInfo.InvariantCulture, out retval))
447 else if (nodeType == typeof (Uri))
450 if (Uri.TryCreate(valuestring, UriKind.RelativeOrAbsolute, out retval))