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