refactor Dictionary class.
[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) 2020 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 /**
35  * The Dictionary template class enables a means of storing key-value
36  * pairs where the keys are strings and the value can be a complex
37  * type.
38  *
39  * It enables lookup of keys via case-insensitive match.
40  */
41
42 using DictionaryKeys = std::vector<std::string>;
43
44 inline void Merge( DictionaryKeys& toDict, const DictionaryKeys& fromDict )
45 {
46   for(const auto& element : fromDict)
47   {
48     auto iter = std::find(toDict.cbegin(), toDict.cend(), element);
49     if(iter == toDict.cend())
50     {
51       toDict.push_back(element);
52     }
53   }
54 }
55
56
57 template<typename EntryType>
58 class Dictionary
59 {
60 private:
61   /**
62    * Element is a key-value pair
63    */
64   struct Element
65   {
66     std::string key;
67     EntryType entry;
68     Element(std::string name, EntryType entry)
69     : key(std::move(name)),
70       entry(std::move(entry))
71     {
72     }
73   };
74   using Elements = std::vector<Element>;
75   Elements container;
76
77   auto FindElementCaseInsensitive(std::string_view key) const
78   {
79     return std::find_if(
80       Begin(), End(), [key](auto& e) { return Dali::CaseInsensitiveStringCompare(e.key, key); });
81   }
82
83   auto FindElement(std::string_view key)
84   {
85     return std::find_if(container.begin(), container.end(), [key](auto& e){
86         return bool(key == e.key);
87       });
88   }
89
90 public:
91   /**
92    * Only allow const iteration over the dictionary
93    */
94   using iterator = typename Elements::const_iterator;
95
96   /**
97    * Constructor
98    */
99   Dictionary<EntryType>() = default;
100
101   /**
102    * Add a key value pair to the dictionary.
103    * If the entry does not already exist, add it to the dictionary
104    */
105   bool Add(std::string name, EntryType entry)
106   {
107     auto iter = FindElement(name);
108     if(iter != End())
109     {
110       return false;
111     }
112
113     container.push_back(Element(std::move(name), std::move(entry)));
114     return true;
115   }
116
117   /**
118    * Add a key-value pair to the dictionary
119    * If the entry does not already exist, add it to the dictionary
120    */
121   bool Add(const char* name, EntryType entry)
122   {
123     if(name != nullptr)
124     {
125       return Add(std::string(name), std::move(entry));
126     }
127     return false;
128   }
129
130   /**
131    * Remove a key value pair from the dictionary.
132    */
133   void Remove(std::string_view name)
134   {
135     if(!name.empty())
136     {
137       auto iter = FindElement(name);
138
139       if(iter != End())
140       {
141         container.erase( iter );
142       }
143     }
144   }
145
146   void Merge( const Dictionary<EntryType>& dictionary )
147   {
148     for(const auto& element : dictionary.container)
149     {
150       auto iter = FindElement(element.key);
151
152       if(iter == End())
153       {
154         container.push_back(Element(element.key, element.entry));
155       }
156       else
157       {
158         iter->entry = element.entry;
159       }
160     }
161   }
162
163   /**
164    * Find the element in the dictionary pointed at by key, and
165    * insensitive search, and return a const pointer to it, or NULL
166    */
167   const EntryType* FindConst(std::string_view key) const
168   {
169     if( ! key.empty() )
170     {
171       auto iter = FindElementCaseInsensitive(key);
172
173       if(iter != End())
174       {
175         return &(iter->entry);
176       }
177     }
178     return nullptr;
179   }
180
181   /**
182    * Find the element in the dictionary pointed at by key using a case
183    * insensitive search, and return a non-const pointer to it, or NULL
184    */
185   EntryType* Find(std::string_view key) const
186   {
187     if( ! key.empty() )
188     {
189       auto iter = FindElementCaseInsensitive(key);
190
191       if(iter != End())
192       {
193         return const_cast<EntryType*>(&(iter->entry));
194       }
195     }
196     return nullptr;
197   }
198
199   iterator Begin() const
200   {
201     return container.cbegin();
202   }
203
204   /**
205    * Return an iterator pointing past the last entry in the dictionary
206    */
207   iterator End() const
208   {
209     return container.cend();
210   }
211
212   void GetKeys( DictionaryKeys& keys ) const
213   {
214     keys.clear();
215     for(const auto& element : container)
216     {
217       keys.push_back(element.key);
218     }
219   }
220
221   void Clear()
222   {
223     container.clear();
224   }
225 };
226
227
228
229 }//Internal
230 }//Toolkit
231 }//Dali
232
233 #endif // DALI_TOOLKIT_INTERNAL_BUILDER_DICTIONARY_H