Conversion to Apache 2.0 license
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / builder / replacement.cpp
1 /*
2  * Copyright (c) 2014 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 // INTERNAL INCLUDES
19 #include <dali-toolkit/internal/builder/replacement.h>
20 #include <dali-toolkit/internal/builder/builder-impl.h>
21 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
22
23 namespace Dali
24 {
25
26 namespace Toolkit
27 {
28
29 namespace Internal
30 {
31
32 namespace  // anon
33 {
34
35 PropertyValueMap::const_iterator FindReplacement( const std::string &str,
36                                                 const PropertyValueMap& overrideMap, const PropertyValueMap& defaultMap )
37 {
38   PropertyValueMap::const_iterator ret  = defaultMap.end();
39
40   PropertyValueMap::const_iterator iter = overrideMap.find( str );
41
42   if( iter != overrideMap.end() )
43   {
44     ret = iter;
45   }
46   else
47   {
48     PropertyValueMap::const_iterator iter = defaultMap.find( str );
49
50     if( iter != defaultMap.end() )
51     {
52       ret = iter;
53     }
54     else
55     {
56       // @ todo
57       // try localized text ie dgettext. Look for colon  {DOMAIN:TEXT} {LC_MESSAGE:ID_XXXX}
58
59     }
60   }
61
62   return ret;
63 }
64
65 std::size_t FirstUnescapedChar(const std::string &initialValue, const std::size_t& startPos, const char c)
66 {
67   std::size_t pos = initialValue.find( c, startPos );
68
69   if(pos > 0)
70   {
71     while( pos != std::string::npos )
72     {
73       if( '\\' == initialValue.at( pos-1 ) )
74       {
75         pos = initialValue.find( c, pos );
76       }
77       else
78       {
79         break;
80       }
81     }
82   }
83
84   return pos;
85 }
86
87 bool GetSubstitutionPosition( const std::string &initialValue, std::size_t &startPos, std::size_t &size )
88 {
89   std::size_t pos = FirstUnescapedChar(initialValue, 0, '{');
90
91   if( std::string::npos == pos )
92   {
93     startPos = std::string::npos;
94     return false;
95   }
96   else
97   {
98     startPos = pos + 1;
99   }
100
101   pos = FirstUnescapedChar(initialValue, startPos, '}');
102
103   if( std::string::npos == pos )
104   {
105     size = std::string::npos;
106     return false;
107   }
108   else
109   {
110     size = pos - startPos;
111   }
112
113   return true;
114 }
115
116 bool ResolvePartialReplacement( const std::string &initialValue, Property::Value &out,
117                                 const PropertyValueMap& overrideMap, const PropertyValueMap& defaultMap )
118 {
119
120   if( initialValue.size() >= 2 )
121   {
122     // eg '{"constants": { "IMAGE_DIR": "/share/images" },
123     //      ...
124     //        "filename":"{IMAGE_DIR}/theme/header.png",
125     //
126     std::size_t startPos = 0;
127     std::size_t size     = std::string::npos;
128
129     if( !GetSubstitutionPosition( initialValue, startPos, size ) )
130     {
131       out = initialValue;
132       return true;
133     }
134     else
135     {
136       const std::string str( initialValue.substr( startPos, size ) );
137
138       PropertyValueMap::const_iterator iter = FindReplacement( str, overrideMap, defaultMap );
139
140       if( iter == defaultMap.end() )
141       {
142         DALI_SCRIPT_WARNING( "Cannot find replacement for '%s'\n", str.c_str() );
143       }
144       else
145       {
146         if( Property::STRING != (*iter).second.GetType() )
147         {
148           DALI_SCRIPT_WARNING( "Cannot replace substring in non string property type='%s'. Initial value '%s'\n",
149                                PropertyTypes::GetName( out.GetType() ), initialValue.c_str() );
150         }
151         else
152         {
153           std::string newString = \
154             initialValue.substr(0, startPos - 1) +
155             (*iter).second.Get<std::string>() +
156             initialValue.substr( startPos + size + 1 );
157
158           return ResolvePartialReplacement( newString, out, overrideMap,  defaultMap );
159         }
160       }
161     }
162   }
163
164   // if we get here we failed
165   return false;
166 }
167
168 } // namespace anon
169
170
171 Replacement::Replacement( const PropertyValueMap& overrideMap, const PropertyValueMap& defaultMap )
172   : mOverrideMap( &overrideMap ), mDefaultMap( &defaultMap )
173 {
174
175 }
176
177 namespace
178 {
179 PropertyValueMap noMap;
180 }
181
182 Replacement::Replacement( const PropertyValueMap& defaultMap )
183   : mOverrideMap( &noMap ), mDefaultMap( &defaultMap )
184 {
185
186 }
187
188 Replacement::Replacement(  )
189   : mOverrideMap( &noMap ), mDefaultMap( &noMap )
190 {
191
192 }
193
194 OptionalString Replacement::HasFullReplacement( const TreeNode & node ) const
195 {
196   OptionalString ret;
197
198   if( node.HasSubstitution() && ((*mOverrideMap).size() || (*mDefaultMap).size()) )
199   {
200     OptionalString v = ::IsString( node );
201     if( v )
202     {
203       const std::string& initialValue = *v;
204       if( (initialValue[ 0 ] == '{') && (initialValue[ initialValue.size() -1 ] == '}') )
205       {
206         ret = initialValue.substr( 1, initialValue.size() - 2 );
207       }
208     }
209   }
210   return ret;
211 }
212
213 Property::Value Replacement::GetFullReplacement( const std::string& replacementString ) const
214 {
215   Property::Value out;
216   DALI_ASSERT_DEBUG( mOverrideMap && "missing map");
217   DALI_ASSERT_DEBUG( mDefaultMap && "missing map");
218
219   PropertyValueMap::const_iterator iter = FindReplacement( replacementString, *mOverrideMap, *mDefaultMap );
220
221   if( iter == (*mDefaultMap).end() )
222   {
223     DALI_SCRIPT_WARNING("Cannot find replacement for '%s'\n", replacementString.c_str());
224   }
225   else
226   {
227     out = (*iter).second;
228 #if defined(DEBUG_ENABLED)
229     DALI_SCRIPT_VERBOSE("  Full replacement for '%s' => to Type '%s'\n",
230                         replacementString.c_str(),
231                         PropertyTypes::GetName( out.GetType()) );
232 #endif
233   }
234
235   return out;
236 }
237
238 OptionalBoolean Replacement::IsBoolean( const TreeNode & node ) const
239 {
240   OptionalBoolean ret;
241   if( OptionalString replace = HasFullReplacement( node ) )
242   {
243     Property::Value value = GetFullReplacement( *replace );
244     if( Property::BOOLEAN == value.GetType() )
245     {
246       ret = value.Get<bool>();
247     }
248   }
249   else
250   {
251     ret = ::IsBoolean( node );
252   }
253   return ret;
254 }
255
256 OptionalBoolean Replacement::IsBoolean( OptionalChild child ) const
257 {
258   if( child )
259   {
260     return IsBoolean( *child );
261   }
262   else
263   {
264     return OptionalBoolean();
265   }
266 }
267
268 // template <typename T, OptionalValue<T> (*ISTYPE)( const TreeNode& node ), Property::Type TYPE>
269 // OptionalValue<T> IsOfType( const TreeNode& node, const PropertyValueMap& overrideMap, const PropertyValueMap& defaultMap )
270 // {
271 //   OptionalValue<T> ret;
272 //   if( OptionalString replace = HasFullReplacement( node, overrideMap, defaultMap ) )
273 //   {
274 //     Property::Value value = GetFullReplacement( *replace, overrideMap, defaultMap );
275 //     if( TYPE == value.GetType() )
276 //     {
277 //       ret = value.Get<T>();
278 //     }
279 //   }
280 //   else
281 //   {
282 //     ret = ISTYPE( node );
283 //   }
284 //   return ret;
285
286 // }
287
288 // OptionalFloat Replacement::IsFloat( const TreeNode & node ) const
289 // {
290 //   return IsOfType<float, ::IsFloat, Property::FLOAT>( node, *mOverrideMap, *mDefaultMap );
291 //   /* OptionalFloat ret; */
292 //   /* if( OptionalString replace = HasFullReplacement( node ) ) */
293 //   /* { */
294 //   /*   Property::Value value = GetFullReplacement( replace ); */
295 //   /*   if( Property::FLOAT == value.GetType() ) */
296 //   /*   { */
297 //   /*     ret = value.Get<float>(); */
298 //   /*   } */
299 //   /* } */
300 //   /* else */
301 //   /* { */
302 //   /*   ret = IsFloat( node ); */
303 //   /* } */
304 //   /* return ret; */
305 // }
306
307 OptionalFloat Replacement::IsFloat( const TreeNode & node ) const
308 {
309   OptionalFloat ret;
310   if( OptionalString replace = HasFullReplacement( node ) )
311   {
312     Property::Value value = GetFullReplacement( *replace );
313     if( Property::FLOAT == value.GetType() )
314     {
315       ret = value.Get<float>();
316     }
317   }
318   else
319   {
320     ret = ::IsFloat( node );
321   }
322   return ret;
323 }
324
325 OptionalString Replacement::IsString( const TreeNode& node ) const
326 {
327   OptionalString ret;
328
329   DALI_ASSERT_DEBUG( mOverrideMap && "missing map");
330   DALI_ASSERT_DEBUG( mDefaultMap && "missing map");
331
332   if( node.HasSubstitution() && ((*mOverrideMap).size() || (*mDefaultMap).size()) )
333   {
334     if( OptionalString v = ::IsString( node ) )
335     {
336       Property::Value value;
337       if( ResolvePartialReplacement( *v, value, *mOverrideMap, *mDefaultMap ) )
338       {
339         if( Property::STRING == value.GetType() )
340         {
341           ret = value.Get<std::string>();
342 #if defined(DEBUG_ENABLED)
343           DALI_SCRIPT_VERBOSE("  Resolved substring replacement for '%s' => '%s'\n", (*v).c_str(), (*ret).c_str());
344 #endif
345         }
346       }
347       else
348       {
349         if( Property::STRING == value.GetType() )
350         {
351           ret = v; // sets the unexpanded. Expansion may occur later in processing with include files
352         }
353       }
354     }
355   }
356   else
357   {
358     ret = ::IsString( node );
359   }
360   return ret;
361 }
362
363 OptionalInteger Replacement::IsInteger( const TreeNode & node ) const
364 {
365   OptionalInteger ret;
366   if( OptionalString replace = HasFullReplacement( node ) )
367   {
368     Property::Value value = GetFullReplacement( *replace );
369     if( Property::INTEGER == value.GetType() )
370     {
371       ret = value.Get<int>();
372     }
373   }
374   else
375   {
376     ret = ::IsInteger( node );
377   }
378   return ret;
379 }
380
381 OptionalVector2 Replacement::IsVector2( const TreeNode & node ) const
382 {
383   OptionalVector2 ret;
384   if( OptionalString replace = HasFullReplacement( node ) )
385   {
386     Property::Value value = GetFullReplacement( *replace );
387     if( Property::VECTOR2 == value.GetType() )
388     {
389       ret = value.Get<Vector2>();
390     }
391   }
392   else
393   {
394     ret = ::IsVector2( node );
395   }
396   return ret;
397 }
398
399 OptionalVector3 Replacement::IsVector3( const TreeNode & node ) const
400 {
401   OptionalVector3 ret;
402   if( OptionalString replace = HasFullReplacement( node ) )
403   {
404     Property::Value value = GetFullReplacement( *replace );
405     if( Property::VECTOR3 == value.GetType() )
406     {
407       ret = value.Get<Vector3>();
408     }
409   }
410   else
411   {
412     ret = ::IsVector3( node );
413   }
414   return ret;
415 }
416
417 OptionalVector4 Replacement::IsVector4( const TreeNode & node ) const
418 {
419   OptionalVector4 ret;
420   if( OptionalString replace = HasFullReplacement( node ) )
421   {
422     Property::Value value = GetFullReplacement( *replace );
423     if( Property::VECTOR4 == value.GetType() )
424     {
425       ret = value.Get<Vector4>();
426     }
427   }
428   else
429   {
430     ret = ::IsVector4( node );
431   }
432   return ret;
433 }
434
435 OptionalMatrix Replacement::IsMatrix( const TreeNode & node ) const
436 {
437   OptionalMatrix ret;
438   if( OptionalString replace = HasFullReplacement( node ) )
439   {
440     Property::Value value = GetFullReplacement( *replace );
441     if( Property::MATRIX == value.GetType() )
442     {
443       ret = value.Get<Matrix>();
444     }
445   }
446   else
447   {
448     ret = ::IsMatrix( node );
449   }
450   return ret;
451 }
452
453 OptionalMatrix3 Replacement::IsMatrix3( const TreeNode & node ) const
454 {
455   OptionalMatrix3 ret;
456   if( OptionalString replace = HasFullReplacement( node ) )
457   {
458     Property::Value value = GetFullReplacement( *replace );
459     if( Property::MATRIX3 == value.GetType() )
460     {
461       ret = value.Get<Matrix3>();
462     }
463   }
464   else
465   {
466     ret = ::IsMatrix3( node );
467   }
468   return ret;
469 }
470
471 OptionalRect Replacement::IsRect( const TreeNode & node ) const
472 {
473   OptionalRect ret;
474   if( OptionalString replace = HasFullReplacement( node ) )
475   {
476     Property::Value value = GetFullReplacement( *replace );
477     if( Property::RECTANGLE == value.GetType() )
478     {
479       ret = value.Get<Rect<int> >();
480     }
481   }
482   else
483   {
484     ret = ::IsRect( node );
485   }
486   return ret;
487 }
488
489
490
491 OptionalFloat Replacement::IsFloat( OptionalChild child ) const
492 {
493   if( child )
494   {
495     return IsFloat( *child );
496   }
497   else
498   {
499     return OptionalFloat();
500   }
501 }
502
503
504 OptionalString Replacement::IsString( OptionalChild child ) const
505 {
506   if( child )
507   {
508     return IsString( *child );
509   }
510   else
511   {
512     return OptionalString();
513   }
514 }
515
516 OptionalInteger Replacement::IsInteger( OptionalChild child ) const
517 {
518   if( child )
519   {
520     return IsInteger( *child );
521   }
522   else
523   {
524     return OptionalInteger();
525   }
526 }
527
528 OptionalVector2 Replacement::IsVector2( OptionalChild child ) const
529 {
530   if( child )
531   {
532     return IsVector2( *child );
533   }
534   else
535   {
536     return OptionalVector2();
537   }
538 }
539
540 OptionalVector3 Replacement::IsVector3( OptionalChild child ) const
541 {
542   if( child )
543   {
544     return IsVector3( *child );
545   }
546   else
547   {
548     return OptionalVector3();
549   }
550 }
551
552 OptionalVector4 Replacement::IsVector4( OptionalChild child ) const
553 {
554   if( child )
555   {
556     return IsVector4( *child );
557   }
558   else
559   {
560     return OptionalVector4();
561   }
562 }
563
564 OptionalMatrix Replacement::IsMatrix( OptionalChild child ) const
565 {
566   if( child )
567   {
568     return IsMatrix( *child );
569   }
570   else
571   {
572     return OptionalMatrix();
573   }
574 }
575
576 OptionalMatrix3 Replacement::IsMatrix3( OptionalChild child ) const
577 {
578   if( child )
579   {
580     return IsMatrix3( *child );
581   }
582   else
583   {
584     return OptionalMatrix3();
585   }
586 }
587
588 OptionalRect Replacement::IsRect( OptionalChild child ) const
589 {
590   if( child )
591   {
592     return IsRect( *child );
593   }
594   else
595   {
596     return OptionalRect();
597   }
598 }
599
600 bool Replacement::IsMap( OptionalChild child, Property::Value& out ) const
601 {
602   bool ret = false;
603
604   if( child )
605   {
606     if( OptionalString replace = HasFullReplacement( *child ) )
607     {
608       out = GetFullReplacement( *replace );
609       if( Property::MAP == out.GetType() )
610       {
611         ret = true;
612       }
613     }
614   }
615
616   return ret;
617 }
618
619 bool Replacement::IsArray( OptionalChild child, Property::Value& out ) const
620 {
621   bool ret = false;
622
623   if( child )
624   {
625     if( OptionalString replace = HasFullReplacement( *child ) )
626     {
627       out = GetFullReplacement( *replace );
628       if( Property::ARRAY == out.GetType() )
629       {
630         ret = true;
631       }
632     }
633   }
634
635   return ret;
636 }
637
638 } // namespace Internal
639
640 } // namespace Toolkit
641
642 } // namespace Dali
643