[dali_2.3.19] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / dictionary.h
1 #ifndef DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H
2 #define DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H
3
4 /*
5  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <dali/public-api/common/vector-wrapper.h>
21
22 #include <algorithm>
23 #include <string_view>
24
25 namespace Dali
26 {
27 extern bool CaseInsensitiveStringCompare(std::string_view a, std::string_view b);
28
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 /**
34  * The Dictionary template class enables a means of storing key-value
35  * pairs where the keys are strings and the value can be a complex
36  * type.
37  *
38  * It enables lookup of keys via case-insensitive match.
39  */
40
41 using DictionaryKeys = std::vector<std::string>;
42
43 inline void Merge(DictionaryKeys& toDict, const DictionaryKeys& fromDict)
44 {
45   for(const auto& element : fromDict)
46   {
47     auto iter = std::find(toDict.cbegin(), toDict.cend(), element);
48     if(iter == toDict.cend())
49     {
50       toDict.push_back(element);
51     }
52   }
53 }
54
55 template<typename EntryType>
56 class Dictionary
57 {
58 private:
59   /**
60    * Element is a key-value pair
61    */
62   struct Element
63   {
64     std::string key;
65     EntryType   entry;
66     Element(std::string name, EntryType entry)
67     : key(std::move(name)),
68       entry(std::move(entry))
69     {
70     }
71   };
72   using Elements = std::vector<Element>;
73   Elements container;
74
75   auto FindElementCaseInsensitive(std::string_view key) const
76   {
77     return std::find_if(
78       Begin(), End(), [key](auto& e) { return Dali::CaseInsensitiveStringCompare(e.key, key); });
79   }
80
81   auto FindElement(std::string_view key)
82   {
83     return std::find_if(container.begin(), container.end(), [key](auto& e) {
84       return bool(key == e.key);
85     });
86   }
87
88 public:
89   /**
90    * Only allow const iteration over the dictionary
91    */
92   using iterator = typename Elements::const_iterator;
93
94   /**
95    * Constructor
96    */
97   Dictionary<EntryType>() = default;
98
99   /**
100    * Add a key value pair to the dictionary.
101    * If the entry does not already exist, add it to the dictionary
102    */
103   bool Add(std::string name, EntryType entry)
104   {
105     auto iter = FindElement(name);
106     if(iter != End())
107     {
108       return false;
109     }
110
111     container.push_back(Element(std::move(name), std::move(entry)));
112     return true;
113   }
114
115   /**
116    * Add a key-value pair to the dictionary
117    * If the entry does not already exist, add it to the dictionary
118    */
119   bool Add(const char* name, EntryType entry)
120   {
121     if(name != nullptr)
122     {
123       return Add(std::string(name), std::move(entry));
124     }
125     return false;
126   }
127
128   /**
129    * Remove a key value pair from the dictionary.
130    */
131   void Remove(std::string_view name)
132   {
133     if(!name.empty())
134     {
135       auto iter = FindElement(name);
136
137       if(iter != End())
138       {
139         container.erase(iter);
140       }
141     }
142   }
143
144   void Merge(const Dictionary<EntryType>& dictionary)
145   {
146     for(const auto& element : dictionary.container)
147     {
148       auto iter = FindElement(element.key);
149
150       if(iter == End())
151       {
152         container.push_back(Element(element.key, element.entry));
153       }
154       else
155       {
156         iter->entry = element.entry;
157       }
158     }
159   }
160
161   /**
162    * Find the element in the dictionary pointed at by key, and
163    * insensitive search, and return a const pointer to it, or NULL
164    */
165   const EntryType* FindConst(std::string_view key) const
166   {
167     if(!key.empty())
168     {
169       auto iter = FindElementCaseInsensitive(key);
170
171       if(iter != End())
172       {
173         return &(iter->entry);
174       }
175     }
176     return nullptr;
177   }
178
179   /**
180    * Find the element in the dictionary pointed at by key using a case
181    * insensitive search, and return a non-const pointer to it, or NULL
182    */
183   EntryType* Find(std::string_view key) const
184   {
185     if(!key.empty())
186     {
187       auto iter = FindElementCaseInsensitive(key);
188
189       if(iter != End())
190       {
191         return const_cast<EntryType*>(&(iter->entry));
192       }
193     }
194     return nullptr;
195   }
196
197   iterator Begin() const
198   {
199     return container.cbegin();
200   }
201
202   /**
203    * Return an iterator pointing past the last entry in the dictionary
204    */
205   iterator End() const
206   {
207     return container.cend();
208   }
209
210   void GetKeys(DictionaryKeys& keys) const
211   {
212     keys.clear();
213     for(const auto& element : container)
214     {
215       keys.push_back(element.key);
216     }
217   }
218
219   void Clear()
220   {
221     container.clear();
222   }
223 };
224
225 } // namespace Internal
226 } // namespace Toolkit
227 } // namespace Dali
228
229 #endif // DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H