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