[dali_1.9.19] Merge branch 'devel/master'
[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     else if(*childType == "extents")
101     {
102       return DeterminePropertyFromNode( *childValue, Dali::Property::EXTENTS, value, replacement);
103     }
104   }
105
106   // else we failed to disambiguate
107   return false;
108 }
109
110
111 bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value)
112 {
113   Replacement noReplacement;
114   return DeterminePropertyFromNode( node, type, value, noReplacement );
115 }
116
117 bool DeterminePropertyFromNode( const TreeNode& node, Property::Type type, Property::Value& value,
118                                 const Replacement& replacer )
119 {
120   bool done = false;
121
122   switch(type)
123   {
124     case Property::BOOLEAN:
125     {
126       if( OptionalBoolean v = replacer.IsBoolean(node) )
127       {
128         value = *v;
129         done = true;
130       }
131       break;
132     }
133     case Property::FLOAT:
134     {
135       if( OptionalFloat v = replacer.IsFloat(node) )
136       {
137         value = *v;
138         done = true;
139       }
140       break;
141     }
142     case Property::INTEGER:
143     {
144       if( OptionalInteger v = replacer.IsInteger(node) )
145       {
146         value = *v;
147         done = true;
148       }
149       break;
150     }
151     case Property::VECTOR2:
152     {
153       if( OptionalVector2 v = replacer.IsVector2(node) )
154       {
155         value = *v;
156         done = true;
157       }
158       break;
159     }
160     case Property::VECTOR3:
161     {
162       if( OptionalVector3 v = replacer.IsVector3(node) )
163       {
164         value = *v;
165         done = true;
166       }
167       break;
168     }
169     case Property::VECTOR4:
170     {
171       if( OptionalVector4 v = replacer.IsVector4(node) )
172       {
173         value = *v;
174         done = true;
175       }
176       else if( OptionalString s = replacer.IsString(node) )
177       {
178         Vector4 color;
179         done = ConvertStringToColor( *s, color );
180         value = color;
181       }
182       else if( TreeNode::OBJECT == node.GetType() )
183       {
184         // check for "r", "g" and "b" child color component nodes
185         OptionalInteger r = replacer.IsInteger( IsChild(node, "r") );
186         OptionalInteger g = replacer.IsInteger( IsChild(node, "g") );
187         OptionalInteger b = replacer.IsInteger( IsChild(node, "b") );
188         if( r && g && b )
189         {
190           float red( (*r) * (1.0f/255.0f) );
191           float green( (*g) * (1.0f/255.0f) );
192           float blue( (*b) * (1.0f/255.0f) );
193           // check for optional "a" (alpha) node, default to fully opaque if it is not found.
194           float alpha( 1.0f );
195           OptionalInteger a = replacer.IsInteger( IsChild(node, "a") );
196           if( a )
197           {
198             alpha = (*a) * (1.0f/255.0f);
199           }
200           value = Vector4( red, green, blue, alpha );
201           done = true;
202         }
203       }
204       break;
205     }
206     case Property::MATRIX3:
207     {
208       if( OptionalMatrix3 v = replacer.IsMatrix3(node) )
209       {
210         value = *v;
211         done = true;
212       }
213       break;
214     }
215     case Property::MATRIX:
216     {
217       if( OptionalMatrix v = replacer.IsMatrix(node) )
218       {
219         value = *v;
220         done = true;
221       }
222       break;
223     }
224     case Property::RECTANGLE:
225     {
226       if( OptionalRect v = replacer.IsRect(node) )
227       {
228         value = *v;
229         done = true;
230       }
231       break;
232     }
233     case Property::ROTATION:
234     {
235       if(4 == node.Size())
236       {
237         if( OptionalVector4 ov = replacer.IsVector4(node) )
238         {
239           const Vector4& v = *ov;
240           // angle, axis as per spec
241           value = Quaternion(Radian(Degree(v[3])),
242                              Vector3(v[0],v[1],v[2]));
243           done = true;
244         }
245       }
246       else
247       {
248         // degrees Euler as per spec
249         if( OptionalVector3 v = replacer.IsVector3(node) )
250         {
251           value = Quaternion(Radian(Degree((*v).x)),
252                              Radian(Degree((*v).y)),
253                              Radian(Degree((*v).z)));
254           done = true;
255         }
256       }
257       break;
258     }
259     case Property::STRING:
260     {
261       if( OptionalString v = replacer.IsString(node) )
262       {
263         value = *v;
264         done = true;
265       }
266       break;
267     }
268     case Property::ARRAY:
269     {
270       if( replacer.IsArray( node, value ) )
271       {
272         done = true;
273       }
274       else if(node.Size())
275       {
276         value = Property::Value(Property::ARRAY);
277         Property::Array* array = value.GetArray();
278
279         unsigned int i = 0;
280         TreeNode::ConstIterator iter(node.CBegin());
281
282         if( array )
283         {
284           for( ; i < node.Size(); ++i, ++iter)
285           {
286             Property::Value childValue;
287             DeterminePropertyFromNode( (*iter).second, childValue, replacer );
288             array->PushBack( childValue );
289           }
290
291           done = ( array->Count() == node.Size() );
292         }
293       }
294       break;
295     }
296     case Property::MAP:
297     {
298       if( replacer.IsMap( node, value ) )
299       {
300         done = true;
301       }
302       else if(node.Size())
303       {
304         value = Property::Value(Property::MAP);
305         Property::Map* map = value.GetMap();
306
307         unsigned int i = 0;
308         TreeNode::ConstIterator iter(node.CBegin());
309
310         if( map )
311         {
312           for( ; i < node.Size(); ++i, ++iter)
313           {
314             Property::Value childValue;
315             DeterminePropertyFromNode( (*iter).second, childValue, replacer );
316             map->Insert( (*iter).first, childValue );
317           }
318
319           done = ( map->Count() == node.Size() );
320         }
321       }
322       break;
323     }
324     case Property::EXTENTS:
325     {
326       if( OptionalExtents v = replacer.IsExtents(node) )
327       {
328         value = *v;
329         done = true;
330       }
331       break;
332     }
333     case Property::NONE:
334     {
335       break;
336     }
337   } // switch type
338
339   return done;
340 }
341
342 void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value )
343
344 {
345   Replacement replacer;
346   DeterminePropertyFromNode( node, value, replacer );
347 }
348
349 void DeterminePropertyFromNode( const TreeNode& node, Property::Value& value,
350                                 const Replacement& replacer )
351 {
352
353   TreeNode::NodeType nodeType = node.GetType();
354
355   // Some values are ambiguous as we have no Property::Type but can be disambiguated in the JSON.
356   // Currently Rotations and Rectangle must always be disambiguated when a type isn't available
357   if( !Disambiguated( node, value, replacer ) )
358   {
359     bool done = false;
360
361     // Here, nodes are handled with the following precedence order:
362     // 1) Nodes with children, that have type ARRAY: Checked for array types including vectors and matrices.
363     // 2) Nodes without children, that do not have type ARRAY OR OBJECT: Checked for primitive types (int / float /etc).
364     // 3) If no match so far; If type is OBJECT: attempt to create as a Property::Map.
365     // 4) If no match still; Create as array.
366
367     // First handle nodes with children.
368     if( node.Size() )
369     {
370       // Handle array types.
371       if( nodeType == TreeNode::ARRAY )
372       {
373         // our current heuristic for deciding an array is actually a vector and not say a map
374         // is to check if the values are all floats
375         bool allNumbers = true;
376         for( TreeConstIter iter = node.CBegin(); iter != node.CEnd(); ++iter )
377         {
378           OptionalFloat checkFloat = IsFloat( ( *iter ).second );
379           if( !checkFloat )
380           {
381             allNumbers = false;
382             break;
383           }
384         }
385
386         if( allNumbers )
387         {
388           // prefer finding vectors over presuming composite Property::Array...
389           if( OptionalMatrix v = IsMatrix( node ) )
390           {
391             value = *v;
392             done = true;
393           }
394           else if( OptionalMatrix3 v = IsMatrix3( node ) )
395           {
396             value = *v;
397             done = true;
398           }
399           else if( OptionalVector4 v = IsVector4( node ) )
400           {
401             value = *v;
402             done = true;
403           }
404           else if( OptionalVector3 v = IsVector3( node ) )
405           {
406             value = *v;
407             done = true;
408           }
409           else if( OptionalVector2 v = IsVector2( node ) )
410           {
411             value = *v;
412             done = true;
413           }
414           else if( 4 == node.Size() )
415           {
416             if( OptionalVector4 v = IsVector4( node ) )
417             {
418               value = *v;
419               done = true;
420             }
421           }
422         }
423       }
424     } // if node.size()
425     else if( ( nodeType != TreeNode::OBJECT ) && ( nodeType != TreeNode::ARRAY ) )
426     {
427       // no children so either one of bool, float, integer, string
428       OptionalBoolean aBool    = replacer.IsBoolean( node );
429       OptionalInteger anInt    = replacer.IsInteger( node );
430       OptionalFloat   aFloat   = replacer.IsFloat( node );
431       OptionalString  aString  = replacer.IsString( node );
432
433       if( aBool )
434       {
435         // a bool is also an int but here we presume int
436         if( anInt )
437         {
438           value = *anInt;
439         }
440         else
441         {
442           value = *aBool;
443         }
444       }
445       else
446       {
447         // Note: These are both floats and strings
448         // {"value":"123"}
449         // {"value":123}
450         // This means we can't have a string with purely numeric content without disambiguation.
451         if( aFloat )
452         {
453           value = *aFloat;
454         }
455         else if( anInt )
456         {
457           value = *anInt;
458         }
459         else
460         {
461           value = *aString;
462         }
463       } // if aBool
464
465       done = true;
466     } // if( node.size() )
467
468     // If we have not created a value so far, attempt to create a Map or Array.
469     if( !done )
470     {
471       // We are guaranteed to have at least one entry as this has been checked already.
472       TreeConstIter containerIterator = node.CBegin();
473       TreeConstIter containerEnd = node.CEnd();
474
475       // The TreeNode::OBJECT type implies a Property::Map.
476       if( nodeType == TreeNode::OBJECT )
477       {
478         // We have a key, treat container as a Map.
479         value = Property::Value( Property::MAP );
480         Property::Map* map = value.GetMap();
481
482         if( map )
483         {
484           // Iterate through container to add all entries.
485           for( ; containerIterator != containerEnd; ++containerIterator )
486           {
487             Property::Value childValue;
488             DeterminePropertyFromNode( ( *containerIterator ).second, childValue, replacer );
489             map->Insert( ( *containerIterator ).first, childValue );
490           }
491         }
492       }
493       else
494       {
495         // We don't have a key, treat container as an array.
496         // Note: We don't check if the node type is array here, as we want to cope with unknowns by creating an array also.
497         // This is the fall-back if no other types could be created.
498         value = Property::Value( Property::ARRAY );
499         Property::Array* array = value.GetArray();
500
501         if( array )
502         {
503           // Iterate through container to add all entries.
504           for( ; containerIterator != containerEnd; ++containerIterator )
505           {
506             Property::Value childValue;
507             DeterminePropertyFromNode( ( *containerIterator ).second, childValue, replacer );
508             array->PushBack( childValue );
509           }
510         }
511       }
512     } // if !done
513   } // if !Disambiguated()
514 }
515
516
517 } // namespace Internal
518
519 } // namespace Toolkit
520
521 } // namespace Dali