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