RPA Toolkit
cb53fc8103b6ae22fdc7f1d2271f24617c4c2ba2
[rpatk.git] / rexjsonrpc / property.h
1 /*
2  * rpcproperty.h
3  *
4  *  Created on: Oct 22, 2019
5  *      Author: mstoilov
6  */
7
8 #ifndef RPCPROPERTY_H_
9 #define RPCPROPERTY_H_
10
11 #include <assert.h>
12 #include <functional>
13 #include "rexjson++.h"
14
15 namespace rexjson {
16
17 class property;
18 using enumarate_children_callback = std::function<void(const std::string&, property&)>;
19
20
21 typedef enum {readonly = 1, writeonly = 2, readwrite = 3} property_access;
22
23 class iproperty_object {
24 public:
25         virtual ~iproperty_object() { }
26         virtual iproperty_object* duplicate() const = 0;
27         virtual void set_prop(const rexjson::value& val) = 0;
28         virtual rexjson::value get_prop() = 0;
29         virtual void enumarate_children(property &parent, const std::string& path, const enumarate_children_callback& callback) = 0;
30         virtual property& navigate(property &parent, const std::string& path) = 0;
31         virtual property& operator[](const std::string& name) = 0;
32         virtual property& push_back(const property& v) = 0;
33         virtual property_access access() const = 0;
34 };
35
36
37 template<typename T>
38 class property_object : public iproperty_object {
39 public:
40         property_object() = delete;
41         property_object(
42                         T* propaddr,
43                         property_access access,
44                         const std::function<void(const rexjson::value&)>& check_hook,
45                         const std::function<void(void *ctx)>& modified_hook,
46                         void* ctx)
47         : propaddr_(propaddr)
48         , access_(access)
49         , check_hook_(check_hook)
50         , modified_hook_(modified_hook)
51         , ctx_(ctx)
52         {
53                 assert(propaddr_);
54         }
55
56         T* propaddr_;
57         property_access access_ = readwrite;
58         std::function<void(const rexjson::value&)> check_hook_ = {};
59         std::function<void(void *ctx)> modified_hook_ = {};
60         void* ctx_ = nullptr;
61
62         virtual iproperty_object* duplicate() const
63         {
64                 return new property_object(propaddr_, access_, check_hook_, modified_hook_, ctx_);
65         }
66
67         virtual void set_prop(const rexjson::value& val) override
68         {
69                 if ((access_ & property_access::writeonly) == 0)
70                         throw std::runtime_error("No write access");
71                 check_hook_(val);
72                 set_prop_impl<T>(propaddr_, val);
73                 modified_hook_(ctx_);
74         }
75
76         virtual rexjson::value get_prop() override
77         {
78                 if ((access_ & property_access::readonly) == 0)
79                         throw std::runtime_error("No read access");
80                 rexjson::value val = get_prop_impl(propaddr_);
81                 return val;
82         }
83
84         virtual void enumarate_children(property &parent, const std::string& path, const enumarate_children_callback& callback) override
85         {
86                 callback(path, parent);
87         }
88
89         virtual property& navigate(property &parent, const std::string& path) override
90         {
91                 if (!path.empty())
92                         throw (std::runtime_error("Invalid path"));
93                 return parent;
94         }
95
96         virtual property& operator[](const std::string& name) override
97         {
98                 throw std::runtime_error("property_object: Invalid call to operator[]");
99         }
100
101         virtual property& push_back(const property& v) override
102         {
103                 throw std::runtime_error("property_object: Invalid call to push_back()");
104         }
105
106         virtual property_access access() const override
107         {
108                 return access_;
109         }
110
111         template <typename U>
112         typename std::enable_if<std::is_integral<U>::value || std::is_enum<U>::value, int64_t>::type
113         get_prop_impl(U* prop)
114         {
115                 return *prop;
116         }
117
118         template <typename U>
119         typename std::enable_if<std::is_floating_point<U>::value, double>::type
120         get_prop_impl(U* prop)
121         {
122                 return *prop;
123         }
124
125
126         template <typename U>
127         typename std::enable_if<std::is_base_of<std::string, U>::value, std::string>::type
128         get_prop_impl(U* prop)
129         {
130                 return *prop;
131         }
132
133         template <typename U>
134         typename std::enable_if<std::is_integral<U>::value, void>::type
135         set_prop_impl(U* prop, const rexjson::value& val)
136         {
137                 if (val.get_type() == rexjson::int_type) {
138                         *prop = val.get_int();
139                 }
140         }
141
142         template <typename U>
143         typename std::enable_if<std::is_floating_point<U>::value, void>::type
144         set_prop_impl(U* prop, const rexjson::value& val)
145         {
146                 *prop = val.get_real();
147         }
148
149         template <typename U>
150         typename std::enable_if<std::is_base_of<std::string, U>::value, void>::type
151         set_prop_impl(U* prop, const rexjson::value& val)
152         {
153                 *prop = val.get_str();
154         }
155
156         template <typename U>
157         typename std::enable_if<std::is_enum<U>::value, void>::type
158         set_prop_impl(U* prop, const rexjson::value& val)
159         {
160                 *reinterpret_cast<typename std::underlying_type<U>::type*>(prop) = val.get_int();
161         }
162 };
163
164 template<>
165 class property_object<bool> : public iproperty_object {
166 public:
167         property_object(
168                         bool* propaddr,
169                         property_access access = property_access::readwrite,
170                         const std::function<void(const rexjson::value&)>& check_hook = {},
171                         const std::function<void(void *ctx)>& modified_hook = {},
172                         void *ctx = nullptr)
173         : propaddr_(propaddr)
174         , access_(access)
175         , check_hook_(check_hook)
176         , modified_hook_(modified_hook)
177         , ctx_(ctx)
178         {}
179
180         bool* propaddr_;
181         property_access access_ = readwrite;
182         std::function<void(const rexjson::value&)> check_hook_ = {};
183         std::function<void(void *ctx)> modified_hook_ = {};
184         void *ctx_ = nullptr;
185
186
187         virtual iproperty_object* duplicate() const
188         {
189                 return new property_object(propaddr_, access_, check_hook_, modified_hook_, ctx_);
190         }
191
192         virtual void set_prop(const rexjson::value& val) override
193         {
194                 if ((access_ & property_access::writeonly) == 0)
195                         throw std::runtime_error("No write access");
196                 check_hook_(val);
197                 *propaddr_ = val.get_bool();
198                 modified_hook_(ctx_);
199         }
200
201         virtual rexjson::value get_prop() override
202         {
203                 if ((access_ & property_access::readonly) == 0)
204                         throw std::runtime_error("No read access");
205                 return *propaddr_;
206         }
207
208         virtual void enumarate_children(property &parent, const std::string& path, const enumarate_children_callback& callback) override
209         {
210                 callback(path, parent);
211         }
212
213         virtual property& navigate(property &parent, const std::string& path) override
214         {
215                 if (!path.empty())
216                         throw (std::runtime_error("Invalid path"));
217                 return parent;
218         }
219
220         virtual property& operator[](const std::string& name) override
221         {
222                 throw std::runtime_error("property_object: Invalid call to operator[]");
223         }
224
225         virtual property& push_back(const property& v) override
226         {
227                 throw std::runtime_error("property_object: Invalid call to push_back()");
228         }
229
230         virtual property_access access() const override
231         {
232                 return access_;
233         }
234
235 };
236
237 template<typename T>
238 class property_range : public property_object<T> {
239 public:
240         using base = property_object<T>;
241
242         property_range() = delete;
243         property_range(
244                         T* propaddr,
245                         size_t size,
246                         property_access access,
247                         const std::function<void(const rexjson::value&)>& check_hook,
248                         const std::function<void(void *ctx)>& modified_hook,
249                         void* ctx)
250         : property_object<T>(propaddr, access, check_hook, modified_hook, ctx)
251         , size_(size)
252         {
253         }
254
255         virtual iproperty_object* duplicate() const
256         {
257                 return new property_range(base::propaddr_, size_, base::access_, base::check_hook_, base::modified_hook_, base::ctx_);
258         }
259
260         virtual void set_prop(const rexjson::value& val) override
261         {
262                 if ((base::access_ & property_access::writeonly) == 0)
263                         throw std::runtime_error("No write access");
264                 base::check_hook_(val);
265                 size_t i = 0;
266                 const rexjson::array& a = val.get_array();
267                 for (auto v : a) {
268                         if (i >= size_)
269                                 throw std::range_error("The array of values passed is of bigger size than the property range.");
270                         base::set_prop_impl(base::propaddr_ + i, v);
271                         ++i;
272                 }
273                 base::modified_hook_(base::ctx_);
274         }
275
276         virtual rexjson::value get_prop() override
277         {
278                 rexjson::array ret;
279                 for (size_t i = 0; i < size_; i++) {
280                         ret.push_back(base::get_prop_impl(base::propaddr_ + i));
281                 }
282                 return ret;
283         }
284
285
286 protected:
287         size_t size_ = 0;
288 };
289 class property_map;
290 class property_array;
291
292 class property {
293 public:
294         virtual ~property();
295         template<typename T>
296         property(
297                         T* propaddr,
298                         property_access access = property_access::readwrite,
299                         const std::function<void(const rexjson::value&)>& check_hook = [](const rexjson::value& v)->void{},
300                         const std::function<void(void *ctx)>& modified_hook = [](void *ctx)->void{},
301                         void *ctx = nullptr)
302         {
303                 object_ = static_cast<iproperty_object*>(new property_object<T>(propaddr, access, check_hook, modified_hook, ctx));
304         }
305
306         template<typename T>
307         property(
308                         T* propaddr,
309                         size_t size,
310                         property_access access = property_access::readwrite,
311                         const std::function<void(const rexjson::value&)>& check_hook = [](const rexjson::value& v)->void{},
312                         const std::function<void(void *ctx)>& modified_hook = [](void *ctx)->void{},
313                         void *ctx = nullptr)
314         {
315                 object_ = static_cast<iproperty_object*>(new property_range<T>(propaddr, size, access, check_hook, modified_hook, ctx));
316         }
317
318         property();
319         property(const property& v);
320         property(property&& v);
321         property(const property_map& map);
322         property(const property_array& array);
323         property& push_back(const property& v);
324         property& operator[](size_t i);
325         property& operator[](const std::string& name);
326         property& operator=(const property& v);
327         property& navigate(const std::string& path);
328         void set_prop(const rexjson::value& val);
329         rexjson::value get_prop();
330         property_access access() const;
331         void enumerate_children(const std::string& path, const enumarate_children_callback& callback);
332         void check_object();
333         rexjson::value to_json();
334
335
336 protected:
337         iproperty_object* object_;
338 };
339
340 class property_map : public iproperty_object {
341 public:
342         property_map() = default;
343         property_map(std::initializer_list<typename std::map<std::string, property>::value_type>lst) : map_(lst) {}
344         property_map(const property_map& map) : map_(map.map_) {}
345         virtual ~property_map() { }
346         virtual iproperty_object* duplicate() const override { return new property_map(*this); }
347         virtual void set_prop(const rexjson::value& val) override { throw std::runtime_error("property_map: Illegal SetProp() call."); }
348         virtual rexjson::value get_prop() override { throw std::runtime_error("property_map: Illegal GetProp() call."); }
349         virtual void enumarate_children(property &parent, const std::string& path, const enumarate_children_callback& callback) override
350         {
351                 for (auto &o : map_) {
352                         o.second.enumerate_children(path + "." + o.first, callback);
353                 }
354         }
355
356         virtual property& navigate(property &parent, const std::string& path) override
357         {
358                 std::string toc = path;
359                 std::size_t toc_pos = toc.find_first_of(".[", 1);
360                 if (toc_pos != std::string::npos)
361                         toc = toc.substr(0, toc_pos);
362                 else
363                         toc = toc.substr(0);
364                 if (toc.empty())
365                         throw (std::runtime_error("Invalid path"));
366                 std::string restpath = path.substr(toc.size());
367                 std::string trimmed = (toc.size() && toc.at(0) == '.') ? toc.substr(1) : toc;
368                 return (operator [](trimmed)).navigate(restpath);
369         }
370
371         virtual property& operator[](const std::string& name)
372         {
373                 return map_.operator [](name);
374         }
375
376         virtual property& push_back(const property& v) override
377         {
378                 throw std::runtime_error("property_map: Invalid call to push_back()");
379         }
380
381         virtual property_access access() const override
382         {
383                 throw std::runtime_error("Invalid call to method access()");
384         }
385
386
387 protected:
388         std::map<std::string, property> map_;
389 };
390
391 class property_array : public iproperty_object {
392 public:
393         property_array() = default;
394         property_array(std::initializer_list<property>lst) : array_(lst) {}
395         property_array(const property_array& array) : array_(array.array_) {}
396         virtual ~property_array() { }
397         virtual iproperty_object* duplicate() const override { return new property_array(*this); }
398         virtual void set_prop(const rexjson::value& val) override { throw std::runtime_error("property_array: Illegal SetProp() call."); }
399         virtual rexjson::value get_prop() override { throw std::runtime_error("property_array: Illegal GetProp() call."); }
400         virtual void enumarate_children(property &parent, const std::string& path, const enumarate_children_callback& callback) override
401         {
402                 size_t i = 0;
403                 for (auto &o : array_) {
404                         o.enumerate_children(path + "[" + std::to_string(i++) + "]", callback);
405                 }
406         }
407
408         virtual property& navigate(property &parent, const std::string& path)
409         {
410                 std::string toc = path;
411                 if (!toc.size() || toc.at(0) != '[')
412                         throw (std::runtime_error("Invalid path"));
413                 std::size_t toc_pos = toc.find_first_of("]", 1);
414                 if (toc_pos == std::string::npos)
415                         throw (std::runtime_error("Invalid path"));
416                 toc = toc.substr(1, toc_pos - 1);
417                 if (toc.empty())
418                         throw (std::runtime_error("Invalid path"));
419                 std::string restpath = path.substr(toc.size() + 2);
420                 return (operator [](toc)).navigate(restpath);
421         }
422
423         virtual property& operator[](const std::string& name)
424         {
425                 size_t idx = atol(name.c_str());
426                 if (idx >= array_.size())
427                         throw std::range_error("Invalid index");
428                 return array_.operator [](idx);
429         }
430
431         virtual property& push_back(const property& v) override
432         {
433                 array_.push_back(v);
434                 return *array_.rbegin();
435         }
436
437         virtual property_access access() const override
438         {
439                 throw std::runtime_error("Invalid call to method access()");
440         }
441
442 protected:
443         std::vector<property> array_;
444 };
445
446 } // namespace rexjson
447
448 #endif /* RPCPROPERTY_H_ */