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