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