1 /*
2  *  Copyright (c) 2012-2014, Bruno Levy
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *  this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright notice,
11  *  this list of conditions and the following disclaimer in the documentation
12  *  and/or other materials provided with the distribution.
13  *  * Neither the name of the ALICE Project-Team nor the names of its
14  *  contributors may be used to endorse or promote products derived from this
15  *  software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  If you modify this software, you should include a notice giving the
30  *  name of the person performing the modification, the date of modification,
31  *  and the reason for such modification.
32  *
33  *  Contact: Bruno Levy
34  *
35  *     Bruno.Levy@inria.fr
36  *     http://www.loria.fr/~levy
37  *
38  *     ALICE Project
39  *     LORIA, INRIA Lorraine,
40  *     Campus Scientifique, BP 239
41  *     54506 VANDOEUVRE LES NANCY CEDEX
42  *     FRANCE
43  *
44  */
45 
46 #ifndef GEOGRAM_BASIC_ENVIRONMENT
47 #define GEOGRAM_BASIC_ENVIRONMENT
48 
49 #include <geogram/basic/common.h>
50 #include <geogram/basic/smart_pointer.h>
51 #include <geogram/basic/counted.h>
52 #include <string>
53 #include <vector>
54 #include <map>
55 
56 /**
57  * \file geogram/basic/environment.h
58  * \brief Provides a mechanism to store global
59  *  variables, retrieve them by their names and
60  *  attach observers to them.
61  */
62 
63 namespace GEO {
64 
65     class Environment;
66 
67     /************************************************************************/
68 
69     /**
70      * \brief Observes Environment variables.
71      * \details VariableObserver offers the possibility to receive
72      * notifications when a variable is changed in an Environment.
73      *
74      * To listen to a variable:
75      * -# Create a class derived class from VariableObserver
76      * and implement function value_changed().
77      * -# Create a MyObserver instance with name "my_variable", it will be
78      * automatically attached to the variable in the root Environment.
79      *
80      * \code
81      * struct MyObserver : VariableObserver {
82      *     MyObserver(const std::string& name) : VariableObserver(name) {}
83      *     virtual void value_changed(const std::string& new_value) {
84      * };
85      * std::auto_ptr<MyObserver> myobserver =
86      *     new MyObserver("my_variable");
87      * \endcode
88      */
89     class GEOGRAM_API VariableObserver {
90     public:
91         /**
92          * \brief Creates a new variable observer.
93          * \details This creates a new observer for variable \p var_name and
94          * automatically adds itself to the variable's observers in the root
95          * environment.
96          * \param[in] var_name name of the variable observed.
97          */
98         VariableObserver(const std::string& var_name);
99 
100         /**
101          * \brief Receives a change notification.
102          * \details This function is called by the Environment when
103          * the variable observed by this observer is modified.
104          * \param[in] new_value the new value of the observed variable.
105          */
106         virtual void value_changed(const std::string& new_value) = 0;
107 
108         /**
109          * \brief Deletes the observer.
110          * \details This automatically removes this observer from the
111          * root environment.
112          */
113         virtual ~VariableObserver();
114 
115         /**
116          * \brief Gets the observed variable.
117          * \return The name of the variable observed by this observer.
118          */
observed_variable()119         const std::string& observed_variable() const {
120             return observed_variable_;
121         }
122 
123     private:
124         std::string observed_variable_;
125         Environment* environment_;
126     };
127 
128     /************************************************************************/
129 
130     /**
131      * \brief List of VariableObserver%s
132      * \details List of variable observers are attached to observed variables
133      * in the Environment%s.
134      */
135     class GEOGRAM_API VariableObserverList {
136     public:
137         /**
138          * \brief Creates an empty list of variable observers.
139          */
VariableObserverList()140         VariableObserverList() :
141             block_notify_(false) {
142         }
143 
144         /**
145          * \brief Notifies all observers in the list.
146          * \param[in] value the value of the variable being changed.
147          */
148         void notify_observers(const std::string& value);
149 
150         /**
151          * \brief Adds an observer to the list.
152          * This adds observer \p observer at the end of the list only if it is
153          * not already present.
154          * \param[in] observer a pointer to the VariableObserver to add.
155          */
156         void add_observer(VariableObserver* observer);
157 
158         /**
159          * \brief Removes an observer from the list.
160          * \param[in] observer a pointer to the VariableObserver to remove.
161          */
162         void remove_observer(VariableObserver* observer);
163 
164     private:
165         /** List of VariableObserver%s */
166         typedef std::vector<VariableObserver*> Observers;
167         Observers observers_;
168         bool block_notify_;
169     };
170 
171     /************************************************************************/
172 
173     /**
174      * \brief Application environment
175      * \details
176      * Environment is a flexible framework for storing and retrieving
177      * application properties. Most important client functions are:
178      * - get_value() to retrieve a property
179      * - set_value() to store a property
180      *
181      * By default, the framework provides a single root Environment that can
182      * be accessed with function instance(). This root environment uses a
183      * dictionary for storing application properties as name-value pairs.
184      *
185      * But developers can define custom Environment classes to access
186      * properties differently. For this, the custom Environment classes must
187      * reimplement low-level access functions get_local_value() and
188      * set_local_value(). For instance, one can redefine low-level functions
189      * to:
190      *
191      * - access properties in a file database
192      * - access properties as system environment variables (see
193      *   SystemEnvironment)
194      * - expose/control a software module configuration:
195      *   - get_local_value() exposes the module configuration as properties
196      *   - set_local_value() allows to control the module configuration
197      *   through properties.
198      *
199      *   This technique is widely used in Vorpaline, for instance:
200      *   - Process has a dedicated environment to control Process
201      *     configuration (multithreading, FPE, ...)
202      *   - Logger also has a dedicated environment to configure the Logger
203      *     behavior, allowed features, ...
204      *
205      * Plugging custom Environment%s in the framework is as simple as adding
206      * the custom Environment as a child of the root Environment with function
207      * add_environment(). Setting a property in an environment affects
208      * this environment locally \b only if no child environment can store the
209      * property. Similarily, retrieving a property from an environment first
210      * checks if the property exists locally, then in all child environments.
211      *
212      * In addition, the Environment framework provides a mechanism for being
213      * notified when a property is modified: VariableObserver%s can be
214      * attached to specific properties to capture modifications of their value
215      * (for more details see VariableObserver).
216      */
217     class GEOGRAM_API Environment : public Counted {
218     public:
219         /**
220          * \brief Gets the root environment
221          * \details If the root environment does not yet exists, it is created
222          * on the fly.
223          * \return A pointer to the root environment
224          */
225         static Environment* instance();
226 
227         /**
228          * \brief Cleans up the environment
229          * \details This destroys the whole root Environment hierarchy.
230          */
231         static void terminate();
232 
233         /**
234          * \brief Adds a child environment
235          * \details Environment \p env is added as a child of this
236          * environment which takes ownership of \p env. The child environment
237          * will be deleted when this environment is deleted.
238          * \param[in] env the child environment
239          * \retval true if the child has been successfully added
240          * \retval false otherwise
241          */
242         virtual bool add_environment(Environment* env);
243 
244         /**
245          * \brief Tests if a variable exists
246          * \param[in] name the name of the variable
247          * \retval true if the variable exists
248          * \retval false otherwise
249          */
250         bool has_value(const std::string& name) const;
251 
252         /**
253          * \brief Retrieves the value of a variable
254          * \details Searches variable \p name and stores its value in the
255          * output string \p value. The function first checks if the variable
256          * exists locally, then in all child environments recursively.
257          * \param[in] name the name of the variable
258          * \param[out] value is set the variable value if it was found
259          * either locally or in a child environment.
260          * \retval true if the variable was found
261          * \retval false otherwise
262          */
263         virtual bool get_value(
264             const std::string& name, std::string& value
265         ) const;
266 
267         /**
268          * \brief Retrieves the value of a variable
269          * \details This is a variant of get_value(name, value) that returns
270          * the variable value directly it it exists. If the variable is not
271          * found, then the function calls abort().
272          * \param[in] name the name of the variable
273          * \return the variable value if it exists.
274          */
275         std::string get_value(const std::string& name) const;
276 
277         /**
278          * \brief Sets a variable value
279          * \details Sets the variable named \p name to the given \p value. The
280          * function first visits all child environments recursively until one
281          * of them accepts the variable. If no child environment can store the
282          * variable, the variable is set locally in this environment. If a
283          * variable is set in an environment, all the variable observers are
284          * notified, starting from the modified environment up to the root
285          * environment.
286          * \param[in] name the name of the variable
287          * \param[in] value the value of the variable
288          * \retval true if the variable was successfully added, either locally
289          * or in a child environment.
290          * \retval false otherwise
291          */
292         virtual bool set_value(
293             const std::string& name, const std::string& value
294         );
295 
296         /**
297          * \brief Finds the environment that declares a variable as
298          *  a local name.
299          * \param[in] name the name of the variable
300          * \return a pointer to the Environment that has \p name as a
301          *  local variable, or nullptr if no such environment exists
302          */
303         virtual Environment* find_environment(const std::string& name);
304 
305         /**
306          * \brief Attaches an observer to a variable
307          * \details Adds observer \p observer to the list of observers
308          * attached to variable \p name. If the observer is already attached
309          * to the variable, the function calls abort(). The environment does
310          * \b not take ownership of the observer, it is the responsibility of
311          * the caller to delete all the variable observers added to an
312          * Environment.
313          * \param[in] name the name of the variable
314          * \param[in] observer a variable observer to add
315          * \retval true if \p observer has been successfully added
316          * \retval false otherwise
317          */
318         virtual bool add_observer(
319             const std::string& name, VariableObserver* observer
320         );
321 
322         /**
323          * \brief Detaches an observer from a variable
324          * \details Removes observer \p observer from the list of observers
325          * attached to variable \p name. If the observer is not attached to
326          * the variable, the function calls abort(). The environment does \b
327          * not delete the removed observer, it is the responsibility of the
328          * caller to delete all the variable observers added to an
329          * Environment.
330          * \param[in] name the name of the variable
331          * \param[in] observer a variable observer to remove
332          * \retval true if \p observer has been successfully removed
333          * \retval false otherwise
334          */
335         virtual bool remove_observer(
336             const std::string& name, VariableObserver* observer
337         );
338 
339         /**
340          * \brief Notifies observers
341          * \details This notifies the observers attached to variable \p
342          * name in this environment, passing them the current value of the
343          * variable. If \p recursive is set to \c true, then the function
344          * recursively notifies observers in the child contexts.
345          * \param[in] name the name of the variable
346          * \param[in] recursive if \c true, notifies observers in the child
347          * contexts. This is \c false by default.
348          * \return \c true
349          */
350         virtual bool notify_observers(
351             const std::string& name, bool recursive = false
352         );
353 
354     protected:
355         /**
356          * \brief Environment destructor
357          * \details This deletes all the child environments, but it does \b
358          * not delete the variable observers.
359          */
360         virtual ~Environment();
361 
362         /**
363          * \brief Retrieves a variable value locally
364          * \details This function is used internally. It searches variable \p
365          * name \b locally and stores its value in the output string \p value.
366          * \param[in] name the name of the variable
367          * \param[out] value is set the variable value if it was found \b
368          * locally.
369          * \retval true if the variable was found
370          * \retval false if not
371          * \note This function must be reimplemented in derived custom
372          * environments.
373          */
374         virtual bool get_local_value(
375             const std::string& name, std::string& value
376         ) const = 0;
377 
378         /**
379          * \brief Sets a variable value locally
380          * \details This function is used internally. It sets the variable
381          * named \p name to the given \p value \b locally.
382          * \param[in] name the name of the variable
383          * \param[in] value the value of the variable
384          * \retval true if the variable was successfully added \b locally
385          * \retval false otherwise
386          * \note This function must be reimplemented in derived custom
387          * environments.
388          */
389         virtual bool set_local_value(
390             const std::string& name, const std::string& value
391         ) = 0;
392 
393         /**
394          * \brief Notifies observers
395          * \details This function is used internally. It notifies the
396          * observers attached to variable \p name in this environment, passing
397          * them the modified value \p value. If \p recursive is \c true, then
398          * the function recursively notifies observers in the child contexts.
399          * \param[in] name the name of the variable
400          * \param[in] value the modified value
401          * \param[in] recursive if \c true, notifies observers in the child
402          * contexts.
403          * \return \c true
404          */
405         bool notify_observers(
406             const std::string& name, const std::string& value,
407             bool recursive
408         );
409 
410         /**
411          * \brief Notifies local observers
412          * \details This function is used internally. It notifies the
413          * observers attached to variable \p name in this environment, passing
414          * them the modified value \p value. Observers in child environments
415          * are \b not notified.
416          * \param[in] name the name of the variable
417          * \param[in] value the modified value
418          * \return \c true
419          */
420         bool notify_local_observers(
421             const std::string& name, const std::string& value
422         );
423 
424     private:
425         /** Smart pointer that contains a Environment object */
426         typedef SmartPointer<Environment> Environment_var;
427 
428         /** List of child environments */
429         typedef std::vector<Environment_var> Environments;
430 
431         /** Stores VariableObserverList indexed by name */
432         typedef std::map<std::string, VariableObserverList> ObserverMap;
433 
434         static Environment_var instance_;
435         Environments environments_;
436         ObserverMap observers_;
437     };
438 
439     /************************************************************************/
440 
441     /**
442      * \brief System environment
443      * \details
444      * This class is a specialization of Environment that retrieves the
445      * variable values from the system environment, but does not allow to set
446      * them.
447      */
448     class SystemEnvironment : public Environment {
449     protected:
450         /** SystemEnvironment destructor */
451         virtual ~SystemEnvironment();
452 
453         /**
454          * \copydoc Environment::set_local_value()
455          * This function does actually \b not update the system environment
456          * and always returns \c false.
457          */
458         virtual bool set_local_value(
459             const std::string& name, const std::string& value
460         );
461 
462         /**
463          * \copydoc Environment::get_local_value()
464          * The value is retrieved from the system environment using the system
465          * function getenv().
466          */
467         virtual bool get_local_value(
468             const std::string& name, std::string& value
469         ) const;
470     };
471 }
472 
473 #endif
474 
475