Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifndef ELEKTRA_KDBCONTEXT_HPP
10 : #define ELEKTRA_KDBCONTEXT_HPP
11 :
12 : #include <kdbmeta.h>
13 : #include <kdbvalue.hpp>
14 :
15 : #include <cassert>
16 : #include <functional>
17 : #include <memory>
18 : #include <set>
19 : #include <string>
20 : #include <unordered_map>
21 : #include <vector>
22 :
23 : namespace kdb
24 : {
25 :
26 108 : class Subject
27 : {
28 : public:
29 : virtual ~Subject () = 0;
30 : typedef std::vector<std::string> Events;
31 : virtual void attachObserver (ValueObserver &);
32 : virtual void attachObserverByEvent (std::string const & event, ValueObserver &);
33 :
34 : /**
35 : * @brief Notify all with given events
36 : *
37 : * @param events the event to be notify (observers need to listen to)
38 : */
39 : virtual void notifyByEvents (Events const & events) const;
40 :
41 : /**
42 : * @brief Notify by every event.
43 : *
44 : * Calls notifyByEvents () with every possible event.
45 : */
46 : virtual void notifyAllEvents () const;
47 :
48 : /**
49 : * @brief Notify on KeySet update.
50 : *
51 : * Not only notify keys that have a layer to update, because
52 : * every value might be updated now.
53 : */
54 : virtual void notifyKeySetUpdate () const;
55 :
56 : protected:
57 : Subject ();
58 :
59 : private:
60 : typedef std::set<ValueObserver::reference> ObserverSet;
61 : ObserverSet m_observers;
62 : mutable std::unordered_map<std::string, ObserverSet> m_events;
63 : };
64 :
65 390 : inline Subject::Subject ()
66 : {
67 : }
68 :
69 462 : inline Subject::~Subject ()
70 : {
71 : }
72 :
73 180 : inline void Subject::attachObserver (ValueObserver & observer)
74 : {
75 360 : m_observers.insert (std::ref (observer));
76 180 : }
77 :
78 236 : inline void Subject::attachObserverByEvent (std::string const & event, ValueObserver & observer)
79 : {
80 472 : auto it = m_events.find (event);
81 472 : if (it == m_events.end ())
82 : {
83 : // add first observer for that event
84 : // (creating a new vector)
85 400 : ObserverSet & os = m_events[event];
86 400 : os.insert (std::ref (observer));
87 : }
88 : else
89 : {
90 108 : it->second.insert (std::ref (observer));
91 : }
92 236 : }
93 :
94 610 : inline void Subject::notifyByEvents (Events const & events) const
95 : {
96 1220 : ObserverSet os;
97 2988 : for (auto & e : events)
98 : {
99 1096 : auto it = m_events.find (e);
100 1096 : if (it != m_events.end ())
101 : {
102 1570 : for (auto & o : it->second)
103 : {
104 412 : os.insert (o); // (discarding duplicates)
105 : }
106 : }
107 : #if DEBUG && VERBOSE
108 : else
109 : {
110 : std::cout << "Trying to notify " << e << " but event does not exist" << std::endl;
111 : }
112 : #endif
113 : }
114 : // now call any observer exactly once
115 2202 : for (auto & o : os)
116 : {
117 372 : o.get ().updateContext ();
118 : }
119 610 : }
120 :
121 4 : inline void Subject::notifyAllEvents () const
122 : {
123 8 : Events events;
124 22 : for (auto & o : m_events)
125 : {
126 10 : events.push_back (o.first);
127 : }
128 4 : notifyByEvents (events);
129 4 : }
130 :
131 24 : inline void Subject::notifyKeySetUpdate () const
132 : {
133 48 : KeySet deps;
134 24 : size_t i = 0;
135 132 : for (auto & o : m_observers)
136 : {
137 120 : Key dep (o.get ().getDepKey ());
138 60 : dep.set<size_t> (i);
139 60 : ++i;
140 60 : deps.append (dep);
141 : }
142 :
143 48 : std::vector<ckdb::Key *> ordered;
144 24 : ordered.resize (deps.size ());
145 24 : int ret = elektraSortTopology (deps.getKeySet (), &ordered[0]);
146 24 : if (ret == 0) throw std::runtime_error ("Cycle in layer dependencies");
147 22 : if (ret == -1) throw std::logic_error ("elektraSortTopology used wrongly");
148 :
149 136 : for (auto & o : ordered)
150 : {
151 336 : std::next (m_observers.begin (), Key (o).get<size_t> ())->get ().updateContext (false);
152 : }
153 22 : }
154 :
155 : /**
156 : * @brief Provides a context for configuration
157 : *
158 : * Is a subject for observers.
159 : *
160 : * Holds currently active layers and allows
161 : * global/scoped activation of layers.
162 : */
163 534 : class Context : public Subject
164 : {
165 : public:
166 520 : Context () : m_active_layers ()
167 : {
168 : }
169 :
170 348 : virtual void execute (Command & c)
171 : {
172 696 : c ();
173 348 : }
174 :
175 : /**
176 : * Lookup value for a current active layer
177 : *
178 : * @param layer the name of the requested layer
179 : * @return the layer
180 : */
181 290 : std::string operator[] (std::string const & layer) const
182 : {
183 580 : auto f = m_active_layers.find (layer);
184 580 : if (f != m_active_layers.end ())
185 : {
186 : assert (f->second && "no null pointers in active_layers");
187 156 : return (*f->second) ();
188 : }
189 268 : return ""; // this line is surprisingly expensive
190 : }
191 :
192 : /**
193 : * @return size of all current layers (to be used with operator[])
194 : */
195 : size_t size () const
196 : {
197 80 : return m_active_layers.size ();
198 : }
199 :
200 : /**
201 : * Attach observer using to all events given by
202 : * its specification (name)
203 : *
204 : * @param key_name the name with placeholders to be used for attaching
205 : * @param observer the observer to attach to
206 : */
207 180 : void attachByName (std::string const & key_name, ValueObserver & observer)
208 : {
209 180 : this->attachObserver (observer);
210 : evaluate (key_name, [&](std::string const & current_id, std::string &, bool) {
211 236 : this->attachObserverByEvent (current_id, observer);
212 : return false;
213 720 : });
214 180 : }
215 :
216 : /**
217 : * Evaluate a specification (name) and return
218 : * a key name under current context
219 : *
220 : * @param key_name the name with placeholders to be evaluated
221 : */
222 640 : std::string evaluate (std::string const & key_name) const
223 : {
224 1552 : return evaluate (key_name, [&](std::string const & current_id, std::string & ret, bool in_group) {
225 4656 : auto f = m_active_layers.find (current_id);
226 1552 : bool left_group = true;
227 3104 : if (f != m_active_layers.end ())
228 : {
229 : assert (f->second && "no null pointers in active_layers");
230 1568 : std::string r = (*f->second) ();
231 784 : if (!r.empty ())
232 : {
233 778 : if (in_group)
234 : {
235 : ret += "%";
236 : }
237 : ret += r;
238 : left_group = false;
239 : }
240 6 : else if (!in_group)
241 : {
242 : ret += "%";
243 : }
244 : }
245 768 : else if (!in_group)
246 : {
247 : ret += "%";
248 : }
249 1552 : return left_group;
250 1920 : });
251 : }
252 :
253 : /**
254 : * Evaluate specification with this context.
255 : *
256 : * @param key_name the keyname with placeholders to evaluate
257 : * @param on_layer the function to be called for every
258 : * placeholder found
259 : *
260 : * @par on_layer is called for every layer in the
261 : * specification.
262 : * @return the evaluated string
263 : */
264 880 : std::string evaluate (std::string const & key_name,
265 : std::function<bool(std::string const &, std::string &, bool in_group)> const & on_layer) const
266 : {
267 880 : size_t const & s = key_name.size ();
268 880 : std::string ret;
269 1760 : std::string current_id;
270 880 : bool capture_id = false; // we are currently within a % block (group or single layer)
271 880 : bool left_group = false; // needed to omit layers that do not matter in a group anymore
272 880 : bool is_in_group = false;
273 :
274 : // heuristic how much too allocate
275 880 : ret.reserve (s * 2);
276 880 : current_id.reserve (32);
277 :
278 52624 : for (std::string::size_type i = 0; i < s; ++i)
279 : {
280 25872 : if (key_name[i] == '%')
281 : {
282 2976 : if (capture_id)
283 : {
284 : // finish capturing
285 1488 : if (!left_group)
286 : {
287 1386 : on_layer (current_id, ret, is_in_group);
288 : }
289 1488 : current_id.clear ();
290 1488 : capture_id = false;
291 : }
292 : else
293 : {
294 : // start capturing
295 : capture_id = true;
296 : left_group = false;
297 : is_in_group = false;
298 : }
299 : }
300 37874 : else if (capture_id && key_name[i] == ' ' && !left_group)
301 : {
302 : // found group separator in active
303 : // group
304 446 : left_group = on_layer (current_id, ret, true);
305 446 : if (!is_in_group && left_group)
306 : {
307 : ret += "%"; // empty groups
308 : }
309 : else
310 : {
311 : is_in_group = true;
312 : }
313 : current_id.clear ();
314 : }
315 : else // non % character
316 : {
317 22450 : if (capture_id)
318 : {
319 14532 : current_id += key_name[i];
320 : }
321 : else
322 : {
323 7918 : ret += key_name[i];
324 : }
325 : }
326 : }
327 :
328 : assert (!capture_id && "number of % incorrect");
329 :
330 880 : return ret;
331 : }
332 :
333 : protected:
334 : // activates layer, records it, but does not notify
335 : template <typename T, typename... Args>
336 90 : void lazyActivate (Args &&... args)
337 : {
338 278 : std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
339 90 : lazyActivateLayer (layer);
340 90 : }
341 :
342 : // needed for with
343 94 : void lazyActivateLayer (std::shared_ptr<Layer> const & layer)
344 : {
345 188 : std::string const & id = layer->id (); // optimisation
346 188 : auto p = m_active_layers.emplace (std::make_pair (id, layer));
347 94 : if (!p.second)
348 : {
349 12 : m_with_stack.push_back (*p.first);
350 6 : p.first->second = layer; // update
351 : }
352 : else
353 : {
354 : // no layer was not active before, remember that
355 352 : m_with_stack.push_back (std::make_pair (id, std::shared_ptr<Layer> ()));
356 : }
357 : #if DEBUG && VERBOSE
358 : std::cout << "lazy activate layer: " << id << std::endl;
359 : #endif
360 94 : }
361 :
362 : void clearAllLayer ()
363 : {
364 0 : m_active_layers.clear ();
365 : }
366 :
367 : // needed for global activation
368 230 : void activateLayer (std::shared_ptr<Layer> const & layer)
369 : {
370 920 : auto p = m_active_layers.emplace (std::make_pair (layer->id (), layer));
371 230 : if (!p.second)
372 : {
373 48 : p.first->second = layer; // update
374 : }
375 :
376 920 : notifyByEvents ({ layer->id () });
377 :
378 : #if DEBUG && VERBOSE
379 : std::cout << "activate layer: " << layer->id () << std::endl;
380 : #endif
381 230 : }
382 :
383 : public:
384 : /**
385 : * @brief Globally activate the layer
386 : *
387 : * @tparam T the layer to activate
388 : * @tparam Args the types for the arguments to pass to layer construction
389 : * @param args the arguments to pass to layer construction
390 : */
391 : template <typename T, typename... Args>
392 94 : std::shared_ptr<Layer> activate (Args &&... args)
393 : {
394 324 : std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
395 94 : activateLayer (layer);
396 94 : return layer;
397 : }
398 :
399 36 : std::shared_ptr<Layer> activate (Wrapped const & value)
400 : {
401 108 : std::shared_ptr<Layer> layer = std::make_shared<WrapLayer> (value);
402 36 : activateLayer (layer);
403 36 : return layer;
404 : }
405 :
406 16 : std::shared_ptr<Layer> activate (std::string key, std::string value)
407 : {
408 48 : std::shared_ptr<Layer> layer = std::make_shared<KeyValueLayer> (key, value);
409 16 : activateLayer (layer);
410 16 : return layer;
411 : }
412 :
413 : protected:
414 : template <typename T, typename... Args>
415 24 : void lazyDeactivate (Args &&... args)
416 : {
417 80 : std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
418 24 : lazyDeactivateLayer (layer);
419 24 : }
420 :
421 24 : void lazyDeactivateLayer (std::shared_ptr<Layer> const & layer)
422 : {
423 72 : auto p = m_active_layers.find (layer->id ());
424 48 : if (p != m_active_layers.end ())
425 : {
426 48 : m_with_stack.push_back (*p);
427 24 : m_active_layers.erase (p);
428 : }
429 : // else: deactivate whats not there:
430 : // nothing to do!
431 : #if DEBUG && VERBOSE
432 : std::cout << "lazy deactivate layer: " << layer->id () << std::endl;
433 : #endif
434 24 : }
435 :
436 42 : void deactivateLayer (std::shared_ptr<Layer> const & layer)
437 : {
438 126 : m_active_layers.erase (layer->id ());
439 :
440 : #if DEBUG && VERBOSE
441 : std::cout << "deactivate layer: " << layer->id () << std::endl;
442 : #endif
443 42 : }
444 :
445 : public:
446 : template <typename T, typename... Args>
447 36 : std::shared_ptr<Layer> deactivate (Args &&... args)
448 : {
449 122 : std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
450 36 : deactivateLayer (layer);
451 144 : notifyByEvents ({ layer->id () });
452 36 : return layer;
453 : }
454 :
455 4 : std::shared_ptr<Layer> deactivate (Wrapped const & value)
456 : {
457 12 : std::shared_ptr<Layer> layer = std::make_shared<WrapLayer> (value);
458 4 : deactivateLayer (layer);
459 16 : notifyByEvents ({ layer->id () });
460 4 : return layer;
461 : }
462 :
463 : std::shared_ptr<Layer> deactivate (std::string key, std::string value)
464 : {
465 : std::shared_ptr<Layer> layer = std::make_shared<KeyValueLayer> (key, value);
466 : deactivateLayer (layer);
467 : notifyByEvents ({ layer->id () });
468 : return layer;
469 : }
470 :
471 : public:
472 : Context & withl (std::shared_ptr<Layer> & l)
473 : {
474 : // build up staple (until function is executed)
475 0 : lazyActivateLayer (l);
476 : return *this;
477 : }
478 :
479 : template <typename T, typename... Args>
480 : Context & with (Args &&... args)
481 : {
482 : // build up staple (until function is executed)
483 90 : lazyActivate<T, Args...> (std::forward<Args> (args)...);
484 : return *this;
485 : }
486 :
487 : template <typename T, typename... Args>
488 : Context & without (Args &&... args)
489 : {
490 : // build up staple (until function is executed)
491 24 : lazyDeactivate<T, Args...> (std::forward<Args> (args)...);
492 : return *this;
493 : }
494 :
495 : Context & operator() (std::function<void()> const & f)
496 : {
497 94 : execHelper (f);
498 :
499 : return *this;
500 : }
501 :
502 : Context & withl (std::shared_ptr<Layer> & l, std::function<void()> const & f)
503 : {
504 4 : lazyActivateLayer (l);
505 4 : execHelper (f);
506 :
507 : return *this;
508 : }
509 :
510 : private:
511 : typedef std::vector<std::pair<std::string, std::shared_ptr<Layer>>> WithStack;
512 :
513 98 : void execHelper (std::function<void()> const & f)
514 : {
515 196 : WithStack with_stack = m_with_stack;
516 196 : m_with_stack.clear (); // allow with to be called recursively
517 : // last step, now lets really activate
518 196 : Subject::Events to_notify;
519 510 : for (auto & s : with_stack)
520 : {
521 118 : to_notify.push_back (s.first);
522 : }
523 98 : notifyByEvents (to_notify);
524 :
525 : // now do the function call,
526 : // keep roll back information on the stack
527 : f ();
528 :
529 : // now roll everything back before all those with()
530 : // and without()
531 334 : while (!with_stack.empty ())
532 : {
533 236 : auto s = with_stack.back ();
534 118 : with_stack.pop_back ();
535 118 : if (!s.second)
536 : {
537 : // do not add null pointer
538 : // but erase layer instead
539 88 : m_active_layers.erase (s.first);
540 : }
541 : else
542 : {
543 60 : auto it = m_active_layers.insert (s);
544 30 : if (!it.second)
545 : {
546 10 : it.first->second = s.second;
547 : }
548 : }
549 : }
550 98 : notifyByEvents (to_notify);
551 98 : }
552 :
553 : std::unordered_map<std::string, std::shared_ptr<Layer>> m_active_layers;
554 : // the with stack holds all layers that were
555 : // changed in the current .with().with()
556 : // invocation chain
557 : WithStack m_with_stack;
558 : };
559 :
560 : template <typename T, typename PolicySetter1 = DefaultPolicyArgs, typename PolicySetter2 = DefaultPolicyArgs,
561 : typename PolicySetter3 = DefaultPolicyArgs, typename PolicySetter4 = DefaultPolicyArgs,
562 : typename PolicySetter5 = DefaultPolicyArgs>
563 : using ContextualValue = Value<T, ContextPolicyIs<Context>, PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4, PolicySetter5>;
564 :
565 : typedef ContextualValue<uint32_t> Integer;
566 : typedef ContextualValue<bool> Boolean;
567 : typedef ContextualValue<std::string> String;
568 :
569 : } // namespace kdb
570 :
571 : #endif
|