Add ScriptUI to support XAML file (#320)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Xaml / XamlNode.cs
1 using System.Collections.Generic;
2 using System.Diagnostics;
3 using System.Linq;
4 using System.Xml;
5 using Tizen.NUI.Binding;
6 using Tizen.NUI.Binding.Internals;
7
8 namespace Tizen.NUI.Xaml
9 {
10     internal interface INode
11     {
12         List<string> IgnorablePrefixes { get; set; }
13
14         IXmlNamespaceResolver NamespaceResolver { get; }
15
16         INode Parent { get; set; }
17
18         void Accept(IXamlNodeVisitor visitor, INode parentNode);
19         INode Clone();
20     }
21
22     internal interface IValueNode : INode
23     {
24     }
25
26     internal interface IElementNode : INode, IListNode
27     {
28         Dictionary<XmlName, INode> Properties { get; }
29         List<XmlName> SkipProperties { get; }
30         INameScope Namescope { get; }
31         XmlType XmlType { get; }
32         string NamespaceURI { get; }
33     }
34
35     internal interface IListNode : INode
36     {
37         List<INode> CollectionItems { get; }
38     }
39
40     [DebuggerDisplay("{NamespaceUri}:{Name}")]
41     internal class XmlType
42     {
43         public XmlType(string namespaceUri, string name, IList<XmlType> typeArguments)
44         {
45             NamespaceUri = namespaceUri;
46             Name = name;
47             TypeArguments = typeArguments;
48         }
49
50         public string NamespaceUri { get; }
51         public string Name { get; }
52         public IList<XmlType> TypeArguments { get; }
53     }
54
55     internal abstract class BaseNode : IXmlLineInfo, INode
56     {
57         protected BaseNode(IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
58         {
59             NamespaceResolver = namespaceResolver;
60             LineNumber = linenumber;
61             LinePosition = lineposition;
62         }
63
64         public IXmlNamespaceResolver NamespaceResolver { get; }
65         public INode Parent { get; set; }
66         public List<string> IgnorablePrefixes { get; set; }
67         public int LineNumber { get; set; }
68         public int LinePosition { get; set; }
69
70         public bool HasLineInfo() => LineNumber >= 0 && LinePosition >= 0;
71
72         public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode);
73         public abstract INode Clone();
74     }
75
76     [DebuggerDisplay("{Value}")]
77     internal class ValueNode : BaseNode, IValueNode
78     {
79         public ValueNode(object value, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
80             : base(namespaceResolver, linenumber, lineposition)
81         {
82             Value = value;
83         }
84
85         public object Value { get; set; }
86
87         public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
88         {
89             visitor.Visit(this, parentNode);
90         }
91
92         public override INode Clone() => new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) {
93             IgnorablePrefixes = IgnorablePrefixes
94         };
95     }
96
97     [DebuggerDisplay("{MarkupString}")]
98     internal class MarkupNode : BaseNode, IValueNode
99     {
100         public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
101             : base(namespaceResolver, linenumber, lineposition)
102         {
103             MarkupString = markupString;
104         }
105
106         public string MarkupString { get; }
107
108         public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
109         {
110             visitor.Visit(this, parentNode);
111         }
112
113         public override INode Clone() => new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) {
114             IgnorablePrefixes = IgnorablePrefixes
115         };
116     }
117
118     [DebuggerDisplay("{XmlType.Name}")]
119     internal class ElementNode : BaseNode, IValueNode, IElementNode
120     {
121         public ElementNode(XmlType type, string namespaceURI, IXmlNamespaceResolver namespaceResolver, int linenumber = -1,
122             int lineposition = -1)
123             : base(namespaceResolver, linenumber, lineposition)
124         {
125             Properties = new Dictionary<XmlName, INode>();
126             SkipProperties = new List<XmlName>();
127             CollectionItems = new List<INode>();
128             XmlType = type;
129             NamespaceURI = namespaceURI;
130         }
131
132         public Dictionary<XmlName, INode> Properties { get; }
133         public List<XmlName> SkipProperties { get; }
134         public List<INode> CollectionItems { get; }
135         public XmlType XmlType { get; }
136         public string NamespaceURI { get; }
137         public INameScope Namescope { get; set; }
138
139         public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
140         {
141             if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
142                 visitor.Visit(this, parentNode);
143
144             if (!SkipChildren(visitor, this, parentNode)) {
145                 foreach (var node in Properties.Values.ToList())
146                     node.Accept(visitor, this);
147                 foreach (var node in CollectionItems)
148                     node.Accept(visitor, this);
149             }
150
151             if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
152                 visitor.Visit(this, parentNode);
153
154         }
155
156         bool IsDataTemplate(INode parentNode)
157         {
158             var parentElement = parentNode as IElementNode;
159             INode createContent;
160             if (parentElement != null &&
161                 parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
162                 createContent == this)
163                 return true;
164             return false;
165         }
166
167         bool IsResourceDictionary() => XmlType.Name == "ResourceDictionary";
168
169         protected bool SkipChildren(IXamlNodeVisitor visitor, INode node, INode parentNode) =>
170                (visitor.StopOnDataTemplate && IsDataTemplate(parentNode))
171             || (visitor.StopOnResourceDictionary && IsResourceDictionary())
172             || visitor.SkipChildren(node, parentNode);
173
174         protected bool SkipVisitNode(IXamlNodeVisitor visitor, INode parentNode) =>
175             !visitor.VisitNodeOnDataTemplate && IsDataTemplate(parentNode);
176
177         public override INode Clone()
178         {
179             var clone = new ElementNode(XmlType, NamespaceURI, NamespaceResolver, LineNumber, LinePosition) {
180                 IgnorablePrefixes = IgnorablePrefixes
181             };
182             foreach (var kvp in Properties)
183                 clone.Properties.Add(kvp.Key, kvp.Value.Clone());
184             foreach (var p in SkipProperties)
185                 clone.SkipProperties.Add(p);
186             foreach (var p in CollectionItems)
187                 clone.CollectionItems.Add(p.Clone());
188             return clone;
189         }
190     }
191
192     internal abstract class RootNode : ElementNode
193     {
194         protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver) : base(xmlType, xmlType.NamespaceUri, nsResolver)
195         {
196         }
197
198         public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
199         {
200             if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
201                 visitor.Visit(this, parentNode);
202
203             if (!SkipChildren(visitor, this, parentNode)) {
204                 foreach (var node in Properties.Values.ToList())
205                     node.Accept(visitor, this);
206                 foreach (var node in CollectionItems)
207                     node.Accept(visitor, this);
208             }
209
210             if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
211                 visitor.Visit(this, parentNode);
212         }
213     }
214
215     internal class ListNode : BaseNode, IListNode, IValueNode
216     {
217         public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
218             : base(namespaceResolver, linenumber, lineposition)
219         {
220             CollectionItems = nodes.ToList();
221         }
222
223         public XmlName XmlName { get; set; }
224         public List<INode> CollectionItems { get; set; }
225
226         public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
227         {
228             if (visitor.VisitingMode == TreeVisitingMode.TopDown)
229                 visitor.Visit(this, parentNode);
230             foreach (var node in CollectionItems)
231                 node.Accept(visitor, this);
232             if (visitor.VisitingMode == TreeVisitingMode.BottomUp)
233                 visitor.Visit(this, parentNode);
234         }
235
236         public override INode Clone()
237         {
238             var items = new List<INode>();
239             foreach (var p in CollectionItems)
240                 items.Add(p.Clone());
241             return new ListNode(items, NamespaceResolver, LineNumber, LinePosition) {
242                 IgnorablePrefixes = IgnorablePrefixes
243             };
244         }
245     }
246
247     internal static class INodeExtensions
248     {
249         public static bool SkipPrefix(this INode node, string prefix)
250         {
251             do {
252                 if (node.IgnorablePrefixes != null && node.IgnorablePrefixes.Contains(prefix))
253                     return true;
254                 node = node.Parent;
255             } while (node != null);
256             return false;
257         }
258     }
259 }