[NUI] Add Tizen.NUI.XamlBuild module
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.XamlBuild / src / public / XamlBuild / ExpandMarkupsVisitor.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Xml;
4 using Tizen.NUI.Xaml;
5
6 namespace Tizen.NUI.Xaml.Build.Tasks
7 {
8     class ExpandMarkupsVisitor : IXamlNodeVisitor
9     {
10         readonly IList<XmlName> skips = new List<XmlName>
11         {
12             XmlName.xKey,
13             XmlName.xTypeArguments,
14             XmlName.xFactoryMethod,
15             XmlName.xName,
16             XmlName.xDataType
17         };
18
19         public ExpandMarkupsVisitor(ILContext context)
20         {
21             Context = context;
22         }
23
24         ILContext Context { get; }
25
26         public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
27         public bool StopOnDataTemplate => false;
28         public bool StopOnResourceDictionary => false;
29         public bool VisitNodeOnDataTemplate => true;
30         public bool SkipChildren(INode node, INode parentNode) => false;
31
32         public bool IsResourceDictionary(ElementNode node)
33         {
34             var parentVar = Context.Variables[(IElementNode)node];
35             return parentVar.VariableType.FullName == "Tizen.NUI.Binding.ResourceDictionary"
36                 || parentVar.VariableType.Resolve().BaseType?.FullName == "Tizen.NUI.Binding.ResourceDictionary";
37         }
38
39         public void Visit(ValueNode node, INode parentNode)
40         {
41         }
42
43         public void Visit(MarkupNode markupnode, INode parentNode)
44         {
45             XmlName propertyName;
46             if (!TryGetProperyName(markupnode, parentNode, out propertyName))
47                 return;
48             if (skips.Contains(propertyName))
49                 return;
50             if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
51                 return;
52             var markupString = markupnode.MarkupString;
53             var node = ParseExpression(ref markupString, Context, markupnode.NamespaceResolver, markupnode) as IElementNode;
54             if (node != null)
55             {
56                 ((IElementNode)parentNode).Properties[propertyName] = node;
57                 node.Accept(new XamlNodeVisitor((n, parent) => n.Parent = parent), parentNode);
58             }
59         }
60
61         public void Visit(ElementNode node, INode parentNode)
62         {
63         }
64
65         public void Visit(RootNode node, INode parentNode)
66         {
67         }
68
69         public void Visit(ListNode node, INode parentNode)
70         {
71         }
72
73         public static bool TryGetProperyName(INode node, INode parentNode, out XmlName name)
74         {
75             name = default(XmlName);
76             var parentElement = parentNode as IElementNode;
77             if (parentElement == null)
78                 return false;
79             foreach (var kvp in parentElement.Properties)
80             {
81                 if (kvp.Value != node)
82                     continue;
83                 name = kvp.Key;
84                 return true;
85             }
86             return false;
87         }
88
89         static INode ParseExpression(ref string expression, ILContext context, IXmlNamespaceResolver nsResolver,
90             IXmlLineInfo xmlLineInfo)
91         {
92             if (expression.StartsWith("{}", StringComparison.Ordinal))
93                 return new ValueNode(expression.Substring(2), null);
94
95             if (expression[expression.Length - 1] != '}')
96                 throw new XamlParseException("Markup expression missing its closing tag", xmlLineInfo);
97
98             int len;
99             string match;
100             if (!MarkupExpressionParser.MatchMarkup(out match, expression, out len))
101                 throw new XamlParseException("Error while parsing markup expression", xmlLineInfo);
102             expression = expression.Substring(len).TrimStart();
103             if (expression.Length == 0)
104                 throw new XamlParseException("Markup expression not closed", xmlLineInfo);
105
106             var provider = new XamlServiceProvider(null, null);
107             provider.Add(typeof (ILContextProvider), new ILContextProvider(context));
108             provider.Add(typeof (IXmlNamespaceResolver), nsResolver);
109             provider.Add(typeof (IXmlLineInfoProvider), new XmlLineInfoProvider(xmlLineInfo));
110
111             return new MarkupExpansionParser().Parse(match, ref expression, provider);
112         }
113
114         class ILContextProvider
115         {
116             public ILContextProvider(ILContext context)
117             {
118                 Context = context;
119             }
120
121             public ILContext Context { get; }
122         }
123
124         class MarkupExpansionParser : MarkupExpressionParser, IExpressionParser<INode>
125         {
126             IElementNode node;
127
128             object IExpressionParser.Parse(string match, ref string remaining, IServiceProvider serviceProvider)
129             {
130                 return Parse(match, ref remaining, serviceProvider);
131             }
132
133             public INode Parse(string match, ref string remaining, IServiceProvider serviceProvider)
134             {
135                 var nsResolver = serviceProvider.GetService(typeof (IXmlNamespaceResolver)) as IXmlNamespaceResolver;
136                 if (nsResolver == null)
137                     throw new ArgumentException();
138                 IXmlLineInfo xmlLineInfo = null;
139                 var xmlLineInfoProvider = serviceProvider.GetService(typeof (IXmlLineInfoProvider)) as IXmlLineInfoProvider;
140                 if (xmlLineInfoProvider != null)
141                     xmlLineInfo = xmlLineInfoProvider.XmlLineInfo;
142                 var contextProvider = serviceProvider.GetService(typeof (ILContextProvider)) as ILContextProvider;
143
144                 var split = match.Split(':');
145                 if (split.Length > 2)
146                     throw new ArgumentException();
147
148                 string prefix, name;
149                 if (split.Length == 2)
150                 {
151                     prefix = split[0];
152                     name = split[1];
153                 }
154                 else
155                 {
156                     prefix = "";
157                     name = split[0];
158                 }
159
160                 var namespaceuri = nsResolver.LookupNamespace(prefix) ?? "";
161                 if (!string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(namespaceuri))
162                     throw new XamlParseException($"Undeclared xmlns prefix '{prefix}'", xmlLineInfo);
163                 //The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
164                 XmlType type;
165                 try
166                 {
167                     type = new XmlType(namespaceuri, name + "Extension", null);
168                     type.GetTypeReference(contextProvider.Context.Module, null);
169                 }
170                 catch (XamlParseException)
171                 {
172                     type = new XmlType(namespaceuri, name, null);
173                 }
174
175                 if (type == null)
176                     throw new NotSupportedException();
177
178                 node = xmlLineInfo == null
179                     ? new ElementNode(type, "", nsResolver)
180                     : new ElementNode(type, "", nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
181
182                 if (remaining.StartsWith("}", StringComparison.Ordinal))
183                 {
184                     remaining = remaining.Substring(1);
185                     return node;
186                 }
187
188                 char next;
189                 string piece;
190                 while ((piece = GetNextPiece(ref remaining, out next)) != null)
191                     HandleProperty(piece, serviceProvider, ref remaining, next != '=');
192
193                 return node;
194             }
195
196             protected override void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
197             {
198                 var nsResolver = serviceProvider.GetService(typeof(IXmlNamespaceResolver)) as IXmlNamespaceResolver;
199                 if (prop != null)
200                 {
201                     var name = new XmlName(node.NamespaceURI, prop);
202                     node.Properties[name] = value as INode ?? new ValueNode(strValue, nsResolver);
203                 }
204                 else //ContentProperty
205                     node.CollectionItems.Add(value as INode ?? new ValueNode(strValue, nsResolver));
206             }
207         }
208     }
209 }