Follow formatting NUI
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / XamlBinding / StyleSheets / Selector.cs
1 using System;
2
3 namespace Tizen.NUI.StyleSheets
4 {
5     internal abstract class Selector
6     {
7         Selector()
8         {
9         }
10
11         public static Selector Parse(CssReader reader, char stopChar = '\0')
12         {
13             Selector root = All, workingRoot = All;
14             Operator workingRootParent = null;
15             Action<Operator, Selector> setCurrentSelector = (op, sel) => SetCurrentSelector(ref root, ref workingRoot, ref workingRootParent, op, sel);
16
17             int p;
18             reader.SkipWhiteSpaces();
19             while ((p = reader.Peek()) > 0)
20             {
21                 switch (unchecked((char)p))
22                 {
23                     case '*':
24                         setCurrentSelector(new And(), All);
25                         reader.Read();
26                         break;
27                     case '.':
28                         reader.Read();
29                         var className = reader.ReadIdent();
30                         if (className == null)
31                             return Invalid;
32                         setCurrentSelector(new And(), new Class(className));
33                         break;
34                     case '#':
35                         reader.Read();
36                         var id = reader.ReadName();
37                         if (id == null)
38                             return Invalid;
39                         setCurrentSelector(new And(), new Id(id));
40                         break;
41                     case '[':
42                         throw new NotImplementedException("Attributes not implemented");
43                     case ',':
44                         reader.Read();
45                         setCurrentSelector(new Or(), All);
46                         reader.SkipWhiteSpaces();
47                         break;
48                     case '+':
49                         reader.Read();
50                         setCurrentSelector(new Adjacent(), All);
51                         reader.SkipWhiteSpaces();
52                         break;
53                     case '~':
54                         reader.Read();
55                         setCurrentSelector(new Sibling(), All);
56                         reader.SkipWhiteSpaces();
57                         break;
58                     case '>':
59                         reader.Read();
60                         setCurrentSelector(new Child(), All);
61                         reader.SkipWhiteSpaces();
62                         break;
63                     case '^':               //not in CSS spec
64                         reader.Read();
65                         var element = reader.ReadIdent();
66                         if (element == null) return Invalid;
67                         setCurrentSelector(new And(), new Base(element));
68                         break;
69                     case ' ':
70                     case '\t':
71                     case '\n':
72                     case '\r':
73                     case '\f':
74                         reader.Read();
75                         bool processWs = false;
76                         while ((p = reader.Peek()) > 0)
77                         {
78                             var c = unchecked((char)p);
79                             if (char.IsWhiteSpace(c))
80                             {
81                                 reader.Read();
82                                 continue;
83                             }
84                             processWs = (c != '+'
85                                         && c != '>'
86                                         && c != ','
87                                         && c != '~'
88                                         && c != '^'
89                                         && c != stopChar);
90                             break;
91                         }
92                         if (!processWs)
93                             break;
94                         setCurrentSelector(new Descendent(), All);
95                         reader.SkipWhiteSpaces();
96                         break;
97                     default:
98                         if (unchecked((char)p) == stopChar)
99                             return root;
100
101                         var elementName = reader.ReadIdent();
102                         if (elementName == null)
103                             return Invalid;
104                         setCurrentSelector(new And(), new Element(elementName));
105                         break;
106                 }
107             }
108             return root;
109         }
110
111         static void SetCurrentSelector(ref Selector root, ref Selector workingRoot, ref Operator workingRootParent, Operator op, Selector sel)
112         {
113             var updateRoot = root == workingRoot;
114
115             op.Left = workingRoot;
116             op.Right = sel;
117             workingRoot = op;
118             if (workingRootParent != null)
119                 workingRootParent.Right = workingRoot;
120
121             if (updateRoot)
122                 root = workingRoot;
123
124             if (workingRoot is Or)
125             {
126                 workingRootParent = (Operator)workingRoot;
127                 workingRoot = sel;
128             }
129         }
130
131         public abstract bool Matches(IStyleSelectable styleable);
132
133         internal static Selector Invalid = new Generic(s => false);
134         internal static Selector All = new Generic(s => true);
135
136         abstract class UnarySelector : Selector
137         {
138         }
139
140         abstract class Operator : Selector
141         {
142             public Selector Left { get; set; } = Invalid;
143             public Selector Right { get; set; } = Invalid;
144         }
145
146         sealed class Generic : UnarySelector
147         {
148             readonly Func<IStyleSelectable, bool> func;
149             public Generic(Func<IStyleSelectable, bool> func)
150             {
151                 this.func = func;
152             }
153
154             public override bool Matches(IStyleSelectable styleable) => func(styleable);
155         }
156
157         sealed class Class : UnarySelector
158         {
159             public Class(string className)
160             {
161                 ClassName = className;
162             }
163
164             public string ClassName { get; }
165             public override bool Matches(IStyleSelectable styleable)
166                 => styleable.Classes != null && styleable.Classes.Contains(ClassName);
167         }
168
169         sealed class Id : UnarySelector
170         {
171             public Id(string id)
172             {
173                 IdName = id;
174             }
175
176             public string IdName { get; }
177             public override bool Matches(IStyleSelectable styleable) => styleable.Id == IdName;
178         }
179
180         sealed class Or : Operator
181         {
182             public override bool Matches(IStyleSelectable styleable) => Right.Matches(styleable) || Left.Matches(styleable);
183         }
184
185         sealed class And : Operator
186         {
187             public override bool Matches(IStyleSelectable styleable) => Right.Matches(styleable) && Left.Matches(styleable);
188         }
189
190         sealed class Element : UnarySelector
191         {
192             public Element(string elementName)
193             {
194                 ElementName = elementName;
195             }
196
197             public string ElementName { get; }
198             public override bool Matches(IStyleSelectable styleable) =>
199                 string.Equals(styleable.NameAndBases[0], ElementName, StringComparison.OrdinalIgnoreCase);
200         }
201
202         sealed class Base : UnarySelector
203         {
204             public Base(string elementName)
205             {
206                 ElementName = elementName;
207             }
208
209             public string ElementName { get; }
210             public override bool Matches(IStyleSelectable styleable)
211             {
212                 for (var i = 0; i < styleable.NameAndBases.Length; i++)
213                     if (string.Equals(styleable.NameAndBases[i], ElementName, StringComparison.OrdinalIgnoreCase))
214                         return true;
215                 return false;
216             }
217         }
218
219         sealed class Child : Operator
220         {
221             public override bool Matches(IStyleSelectable styleable) =>
222                 Right.Matches(styleable) && styleable.Parent != null && Left.Matches(styleable.Parent);
223         }
224
225         sealed class Descendent : Operator
226         {
227             public override bool Matches(IStyleSelectable styleable)
228             {
229                 if (!Right.Matches(styleable))
230                     return false;
231                 var parent = styleable.Parent;
232                 while (parent != null)
233                 {
234                     if (Left.Matches(parent))
235                         return true;
236                     parent = parent.Parent;
237                 }
238                 return false;
239             }
240         }
241
242         sealed class Adjacent : Operator
243         {
244             public override bool Matches(IStyleSelectable styleable)
245             {
246                 if (!Right.Matches(styleable))
247                     return false;
248                 if (styleable.Parent == null)
249                     return false;
250
251                 IStyleSelectable prev = null;
252                 foreach (var elem in styleable.Parent.Children)
253                 {
254                     if (elem == styleable && prev != null)
255                         return Left.Matches(prev);
256                     prev = elem;
257                 }
258                 return false;
259                 //var index = styleable.Parent.Children.IndexOf(styleable);
260                 //if (index == 0)
261                 //      return false;
262                 //var adjacent = styleable.Parent.Children[index - 1];
263                 //return Left.Matches(adjacent);
264             }
265         }
266
267         sealed class Sibling : Operator
268         {
269             public override bool Matches(IStyleSelectable styleable)
270             {
271                 if (!Right.Matches(styleable))
272                     return false;
273                 if (styleable.Parent == null)
274                     return false;
275
276                 int selfIndex = 0;
277                 bool foundSelfInParent = false;
278                 foreach (var elem in styleable.Parent.Children)
279                 {
280                     if (elem == styleable)
281                     {
282                         foundSelfInParent = true;
283                         break;
284                     }
285                     ++selfIndex;
286                 }
287
288                 if (!foundSelfInParent)
289                     return false;
290
291                 int index = 0;
292                 foreach (var elem in styleable.Parent.Children)
293                 {
294                     if (index >= selfIndex)
295                         return false;
296                     if (Left.Matches(elem))
297                         return true;
298                     ++index;
299                 }
300
301                 return false;
302
303                 //var index = styleable.Parent.Children.IndexOf(styleable);
304                 //if (index == 0)
305                 //      return false;
306                 //int siblingIndex = -1;
307                 //for (var i = 0; i < index; i++)
308                 //      if (Left.Matches(styleable.Parent.Children[i])) {
309                 //              siblingIndex = i;
310                 //              break;
311                 //      }
312                 //return siblingIndex != -1;
313             }
314         }
315     }
316 }