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