[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / builder-set-property.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // EXTERNAL INCLUDES
19 #include <dali/public-api/object/property-array.h>
20 #include <dali/public-api/object/property-map.h>
21 #include <sstream>
22
23 // INTERNAL INCLUDES
24 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
25 #include <dali-toolkit/internal/builder/builder-impl.h>
26 #include <dali-toolkit/internal/builder/builder-set-property.h>
27 #include <dali-toolkit/internal/builder/replacement.h>
28 #include <dali-toolkit/internal/helpers/color-conversion.h>
29
30 namespace Dali
31 {
32 namespace Toolkit
33 {
34 namespace Internal
35 {
36 /**
37  * A property value type can be forced when its unknown by a disambiguation convention in the json
38  * ie  "myarray": [1,2,3,4] ; would be a vector but
39  *     "myarray": {"typeCast":"array", "value":[1,2,3,4]} would be an array
40  * @param child The node whose string to search for a disambiguated type
41  * @param value The value to set
42  * @param replacement The user overriding constant map
43  * @return True if child contained a disambiguated string that could be converted.
44  */
45 bool Disambiguated(const TreeNode&        child,
46                    Dali::Property::Value& value,
47                    const Replacement&     replacement)
48 {
49   OptionalString childType  = IsString(IsChild(child, "typeCast"));
50   OptionalChild  childValue = IsChild(child, "value");
51
52   if(childType && childValue && (2 == child.Size()))
53   {
54     // this case allows disambiguation but normally the type is guessed
55     // 2 == child.count() is an extra check as the user could have a user dictionary/map with
56     // type-cast and value keys. If they do then a work around is to add a bogus key to not run this case.
57     if(*childType == "boolean")
58     {
59       return DeterminePropertyFromNode(*childValue, Dali::Property::BOOLEAN, value, replacement);
60     }
61     else if(*childType == "float")
62     {
63       return DeterminePropertyFromNode(*childValue, Dali::Property::FLOAT, value, replacement);
64     }
65     else if(*childType == "vector2")
66     {
67       return DeterminePropertyFromNode(*childValue, Dali::Property::VECTOR2, value, replacement);
68     }
69     else if(*childType == "vector3")
70     {
71       return DeterminePropertyFromNode(*childValue, Dali::Property::VECTOR3, value, replacement);
72     }
73     else if(*childType == "vector4")
74     {
75       return DeterminePropertyFromNode(*childValue, Dali::Property::VECTOR4, value, replacement);
76     }
77     else if(*childType == "rotation")
78     {
79       return DeterminePropertyFromNode(*childValue, Dali::Property::ROTATION, value, replacement);
80     }
81     else if(*childType == "rect")
82     {
83       return DeterminePropertyFromNode(*childValue, Dali::Property::RECTANGLE, value, replacement);
84     }
85     else if(*childType == "string")
86     {
87       return DeterminePropertyFromNode(*childValue, Dali::Property::STRING, value, replacement);
88     }
89     else if(*childType == "map")
90     {
91       return DeterminePropertyFromNode(*childValue, Dali::Property::MAP, value, replacement);
92     }
93     else if(*childType == "array")
94     {
95       return DeterminePropertyFromNode(*childValue, Dali::Property::ARRAY, value, replacement);
96     }
97     else if(*childType == "extents")
98     {
99       return DeterminePropertyFromNode(*childValue, Dali::Property::EXTENTS, value, replacement);
100     }
101   }
102
103   // else we failed to disambiguate
104   return false;
105 }
106
107 bool DeterminePropertyFromNode(const TreeNode& node, Property::Type type, Property::Value& value)
108 {
109   Replacement noReplacement;
110   return DeterminePropertyFromNode(node, type, value, noReplacement);
111 }
112
113 bool DeterminePropertyFromNode(const TreeNode& node, Property::Type type, Property::Value& value, const Replacement& replacer)
114 {
115   bool done = false;
116
117   switch(type)
118   {
119     case Property::BOOLEAN:
120     {
121       if(OptionalBoolean v = replacer.IsBoolean(node))
122       {
123         value = *v;
124         done  = true;
125       }
126       break;
127     }
128     case Property::FLOAT:
129     {
130       if(OptionalFloat v = replacer.IsFloat(node))
131       {
132         value = *v;
133         done  = true;
134       }
135       break;
136     }
137     case Property::INTEGER:
138     {
139       if(OptionalInteger v = replacer.IsInteger(node))
140       {
141         value = *v;
142         done  = true;
143       }
144       break;
145     }
146     case Property::VECTOR2:
147     {
148       if(OptionalVector2 v = replacer.IsVector2(node))
149       {
150         value = *v;
151         done  = true;
152       }
153       break;
154     }
155     case Property::VECTOR3:
156     {
157       if(OptionalVector3 v = replacer.IsVector3(node))
158       {
159         value = *v;
160         done  = true;
161       }
162       break;
163     }
164     case Property::VECTOR4:
165     {
166       if(OptionalVector4 v = replacer.IsVector4(node))
167       {
168         value = *v;
169         done  = true;
170       }
171       else if(OptionalString s = replacer.IsString(node))
172       {
173         Vector4 color;
174         done  = ConvertStringToColor(*s, color);
175         value = color;
176       }
177       else if(TreeNode::OBJECT == node.GetType())
178       {
179         // check for "r", "g" and "b" child color component nodes
180         OptionalInteger r = replacer.IsInteger(IsChild(node, "r"));
181         OptionalInteger g = replacer.IsInteger(IsChild(node, "g"));
182         OptionalInteger b = replacer.IsInteger(IsChild(node, "b"));
183         if(r && g && b)
184         {
185           float red((*r) * (1.0f / 255.0f));
186           float green((*g) * (1.0f / 255.0f));
187           float blue((*b) * (1.0f / 255.0f));
188           // check for optional "a" (alpha) node, default to fully opaque if it is not found.
189           float           alpha(1.0f);
190           OptionalInteger a = replacer.IsInteger(IsChild(node, "a"));
191           if(a)
192           {
193             alpha = (*a) * (1.0f / 255.0f);
194           }
195           value = Vector4(red, green, blue, alpha);
196           done  = true;
197         }
198       }
199       break;
200     }
201     case Property::MATRIX3:
202     {
203       if(OptionalMatrix3 v = replacer.IsMatrix3(node))
204       {
205         value = *v;
206         done  = true;
207       }
208       break;
209     }
210     case Property::MATRIX:
211     {
212       if(OptionalMatrix v = replacer.IsMatrix(node))
213       {
214         value = *v;
215         done  = true;
216       }
217       break;
218     }
219     case Property::RECTANGLE:
220     {
221       if(OptionalRect v = replacer.IsRect(node))
222       {
223         value = *v;
224         done  = true;
225       }
226       break;
227     }
228     case Property::ROTATION:
229     {
230       if(4 == node.Size())
231       {
232         if(OptionalVector4 ov = replacer.IsVector4(node))
233         {
234           const Vector4& v = *ov;
235           // angle, axis as per spec
236           value = Quaternion(Radian(Degree(v[3])),
237                              Vector3(v[0], v[1], v[2]));
238           done  = true;
239         }
240       }
241       else
242       {
243         // degrees Euler as per spec
244         if(OptionalVector3 v = replacer.IsVector3(node))
245         {
246           value = Quaternion(Radian(Degree((*v).x)),
247                              Radian(Degree((*v).y)),
248                              Radian(Degree((*v).z)));
249           done  = true;
250         }
251       }
252       break;
253     }
254     case Property::STRING:
255     {
256       if(OptionalString v = replacer.IsString(node))
257       {
258         value = *v;
259         done  = true;
260       }
261       break;
262     }
263     case Property::ARRAY:
264     {
265       if(replacer.IsArray(node, value))
266       {
267         done = true;
268       }
269       else if(node.Size())
270       {
271         value                  = Property::Value(Property::ARRAY);
272         Property::Array* array = value.GetArray();
273
274         unsigned int            i = 0;
275         TreeNode::ConstIterator iter(node.CBegin());
276
277         if(array)
278         {
279           for(; i < node.Size(); ++i, ++iter)
280           {
281             Property::Value childValue;
282             DeterminePropertyFromNode((*iter).second, childValue, replacer);
283             array->PushBack(childValue);
284           }
285
286           done = (array->Count() == node.Size());
287         }
288       }
289       break;
290     }
291     case Property::MAP:
292     {
293       if(replacer.IsMap(node, value))
294       {
295         done = true;
296       }
297       else if(node.Size())
298       {
299         value              = Property::Value(Property::MAP);
300         Property::Map* map = value.GetMap();
301
302         unsigned int            i = 0;
303         TreeNode::ConstIterator iter(node.CBegin());
304
305         if(map)
306         {
307           for(; i < node.Size(); ++i, ++iter)
308           {
309             Property::Value childValue;
310             DeterminePropertyFromNode((*iter).second, childValue, replacer);
311             map->Insert((*iter).first, childValue);
312           }
313
314           done = (map->Count() == node.Size());
315         }
316       }
317       break;
318     }
319     case Property::EXTENTS:
320     {
321       if(OptionalExtents v = replacer.IsExtents(node))
322       {
323         value = *v;
324         done  = true;
325       }
326       break;
327     }
328     case Property::NONE:
329     {
330       break;
331     }
332   } // switch type
333
334   return done;
335 }
336
337 void DeterminePropertyFromNode(const TreeNode& node, Property::Value& value)
338
339 {
340   Replacement replacer;
341   DeterminePropertyFromNode(node, value, replacer);
342 }
343
344 void DeterminePropertyFromNode(const TreeNode& node, Property::Value& value, const Replacement& replacer)
345 {
346   TreeNode::NodeType nodeType = node.GetType();
347
348   // Some values are ambiguous as we have no Property::Type but can be disambiguated in the JSON.
349   // Currently Rotations and Rectangle must always be disambiguated when a type isn't available
350   if(!Disambiguated(node, value, replacer))
351   {
352     bool done = false;
353
354     // Here, nodes are handled with the following precedence order:
355     // 1) Nodes with children, that have type ARRAY: Checked for array types including vectors and matrices.
356     // 2) Nodes without children, that do not have type ARRAY OR OBJECT: Checked for primitive types (int / float /etc).
357     // 3) If no match so far; If type is OBJECT: attempt to create as a Property::Map.
358     // 4) If no match still; Create as array.
359
360     // First handle nodes with children.
361     if(node.Size())
362     {
363       // Handle array types.
364       if(nodeType == TreeNode::ARRAY)
365       {
366         // our current heuristic for deciding an array is actually a vector and not say a map
367         // is to check if the values are all floats
368         bool allNumbers = true;
369         for(TreeConstIter iter = node.CBegin(); iter != node.CEnd(); ++iter)
370         {
371           OptionalFloat checkFloat = IsFloat((*iter).second);
372           if(!checkFloat)
373           {
374             allNumbers = false;
375             break;
376           }
377         }
378
379         if(allNumbers)
380         {
381           // prefer finding vectors over presuming composite Property::Array...
382           if(OptionalMatrix v = IsMatrix(node))
383           {
384             value = *v;
385             done  = true;
386           }
387           else if(OptionalMatrix3 v = IsMatrix3(node))
388           {
389             value = *v;
390             done  = true;
391           }
392           else if(OptionalVector4 v = IsVector4(node))
393           {
394             value = *v;
395             done  = true;
396           }
397           else if(OptionalVector3 v = IsVector3(node))
398           {
399             value = *v;
400             done  = true;
401           }
402           else if(OptionalVector2 v = IsVector2(node))
403           {
404             value = *v;
405             done  = true;
406           }
407           else if(4 == node.Size())
408           {
409             if(OptionalVector4 v = IsVector4(node))
410             {
411               value = *v;
412               done  = true;
413             }
414           }
415         }
416       }
417     } // if node.size()
418     else if((nodeType != TreeNode::OBJECT) && (nodeType != TreeNode::ARRAY))
419     {
420       // no children so either one of bool, float, integer, string
421       OptionalBoolean aBool   = replacer.IsBoolean(node);
422       OptionalInteger anInt   = replacer.IsInteger(node);
423       OptionalFloat   aFloat  = replacer.IsFloat(node);
424       OptionalString  aString = replacer.IsString(node);
425
426       if(aBool)
427       {
428         // a bool is also an int but here we presume int
429         if(anInt)
430         {
431           value = *anInt;
432         }
433         else
434         {
435           value = *aBool;
436         }
437       }
438       else
439       {
440         // Note: These are both floats and strings
441         // {"value":"123"}
442         // {"value":123}
443         // This means we can't have a string with purely numeric content without disambiguation.
444         if(aFloat)
445         {
446           value = *aFloat;
447         }
448         else if(anInt)
449         {
450           value = *anInt;
451         }
452         else
453         {
454           value = *aString;
455         }
456       } // if aBool
457
458       done = true;
459     } // if( node.size() )
460
461     // If we have not created a value so far, attempt to create a Map or Array.
462     if(!done)
463     {
464       // We are guaranteed to have at least one entry as this has been checked already.
465       TreeConstIter containerIterator = node.CBegin();
466       TreeConstIter containerEnd      = node.CEnd();
467
468       // The TreeNode::OBJECT type implies a Property::Map.
469       if(nodeType == TreeNode::OBJECT)
470       {
471         // We have a key, treat container as a Map.
472         value              = Property::Value(Property::MAP);
473         Property::Map* map = value.GetMap();
474
475         if(map)
476         {
477           // Iterate through container to add all entries.
478           for(; containerIterator != containerEnd; ++containerIterator)
479           {
480             Property::Value childValue;
481             DeterminePropertyFromNode((*containerIterator).second, childValue, replacer);
482             map->Insert((*containerIterator).first, childValue);
483           }
484         }
485       }
486       else
487       {
488         // We don't have a key, treat container as an array.
489         // Note: We don't check if the node type is array here, as we want to cope with unknowns by creating an array also.
490         // This is the fall-back if no other types could be created.
491         value                  = Property::Value(Property::ARRAY);
492         Property::Array* array = value.GetArray();
493
494         if(array)
495         {
496           // Iterate through container to add all entries.
497           for(; containerIterator != containerEnd; ++containerIterator)
498           {
499             Property::Value childValue;
500             DeterminePropertyFromNode((*containerIterator).second, childValue, replacer);
501             array->PushBack(childValue);
502           }
503         }
504       }
505     } // if !done
506   }   // if !Disambiguated()
507 }
508
509 } // namespace Internal
510
511 } // namespace Toolkit
512
513 } // namespace Dali