Panda3D
configVariableCore.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file configVariableCore.cxx
10  * @author drose
11  * @date 2004-10-15
12  */
13 
14 #include "configVariableCore.h"
15 #include "configDeclaration.h"
16 #include "configPage.h"
17 #include "pset.h"
18 #include "pnotify.h"
19 #include "config_prc.h"
20 
21 // This file is generated by ppremake.
22 #include "prc_parameters.h"
23 
24 #include <algorithm>
25 
26 using std::string;
27 
28 
29 /**
30  * Use the ConfigVariableManager::make_variable() interface to create a new
31  * ConfigVariableCore.
32  */
33 ConfigVariableCore::
34 ConfigVariableCore(const string &name) :
35  _name(name),
36  _is_used(false),
37  _value_type(VT_undefined),
38  _flags(0),
39  _default_value(nullptr),
40  _local_value(nullptr),
41  _declarations_sorted(true),
42  _value_queried(false)
43 {
44 #if defined(PRC_INC_TRUST_LEVEL) && PRC_INC_TRUST_LEVEL != 0
45  _flags = (_flags & ~F_trust_level_mask) | ((_flags & F_trust_level_mask) + PRC_INC_TRUST_LEVEL);
46 #endif // PRC_INC_TRUST_LEVEL
47 }
48 
49 /**
50  * This is used by ConfigVariableManager to create the variable from a
51  * template--basically, another variable with all of the initial properties
52  * pre-defined.
53  */
54 ConfigVariableCore::
55 ConfigVariableCore(const ConfigVariableCore &templ, const string &name) :
56  _name(name),
57  _is_used(templ._is_used),
58  _value_type(templ._value_type),
59  _description(templ._description),
60  _flags(templ._flags),
61  _default_value(nullptr),
62  _local_value(nullptr),
63  _declarations_sorted(false),
64  _value_queried(false)
65 {
66  if (templ._default_value != nullptr) {
67  set_default_value(templ._default_value->get_string_value());
68  }
69 }
70 
71 /**
72  * The destructor should never be called; ConfigVariableCore objects live
73  * forever and never get destructed.
74  */
75 ConfigVariableCore::
76 ~ConfigVariableCore() {
77  prc_cat->error()
78  << "Internal error--ConfigVariableCore destructor called!\n";
79 }
80 
81 /**
82  * Specifies the type of this variable. See get_value_type(). It is not an
83  * error to call this multiple times, but if the value changes once
84  * get_declaration() has been called, a warning is printed.
85  */
87 set_value_type(ConfigVariableCore::ValueType value_type) {
88  if (_value_queried && _value_type != value_type) {
89  if ((_flags & F_dconfig) != 0) {
90  // As a special exception, if the flags include F_dconfig, we don't
91  // report a warning for changing the type, assuming the variable is
92  // being defined through the older DConfig interface.
93 
94  } else {
95  prc_cat->warning()
96  << "changing type for ConfigVariable "
97  << get_name() << " from " << _value_type << " to "
98  << value_type << ".\n";
99  }
100  }
101 
102  _value_type = value_type;
103 }
104 
105 /**
106  * Specifies the trust level of this variable. See get_flags(). It is not an
107  * error to call this multiple times, but if the value changes once
108  * get_declaration() has been called, a warning is printed.
109  */
111 set_flags(int flags) {
112  if ((flags & F_dconfig) != 0) {
113  flags = (flags & ~F_trust_level_mask) | PRC_DCONFIG_TRUST_LEVEL;
114  }
115 
116 #if defined(PRC_INC_TRUST_LEVEL) && PRC_INC_TRUST_LEVEL != 0
117  flags = (flags & ~F_trust_level_mask) | ((flags & F_trust_level_mask) + PRC_INC_TRUST_LEVEL);
118 #endif // PRC_INC_TRUST_LEVEL
119 
120  if (_value_queried) {
121  int bits_changed = (_flags ^ flags);
122  if ((bits_changed & F_trust_level_mask) != 0) {
123  prc_cat->warning()
124  << "changing trust level for ConfigVariable "
125  << get_name() << " from " << (_flags & F_trust_level_mask) << " to "
126  << (flags & F_trust_level_mask) << ".\n";
127  }
128  if ((bits_changed & ~(F_trust_level_mask | F_dconfig)) != 0) {
129  prc_cat->warning()
130  << "changing flags for ConfigVariable "
131  << get_name() << " from " << std::hex
132  << (_flags & ~F_trust_level_mask) << " to "
133  << (flags & ~F_trust_level_mask) << std::dec << ".\n";
134  }
135  }
136 
137  _flags = flags;
138 
139  // Changing the trust level will require re-sorting the declarations.
140  _declarations_sorted = false;
141 }
142 
143 /**
144  * Specifies the one-line description of this variable. See
145  * get_description(). It is not an error to call this multiple times, but if
146  * the value changes once get_declaration() has been called, a warning is
147  * printed.
148  */
150 set_description(const string &description) {
151  if (_value_queried && _description != description) {
152  if ((_flags & F_dconfig) != 0) {
153  // As a special exception, if the flags include F_dconfig, we don't
154  // change it, since this is presumably coming from the older DConfig
155  // interface.
156  return;
157  }
158  if (description == "DConfig") {
159  // As a similar exception, we don't replace an existing description with
160  // one that reads simply "DConfig", unless it was empty previously.
161  if (_description.empty()) {
162  _description = description;
163  }
164  return;
165  }
166 
167  if (description.empty()) {
168  // If the new description is empty, we don't do anything.
169  return;
170  }
171 
172  if (_description.empty()) {
173  // If the previous description was empty, we quietly replace it.
174  _description = description;
175  return;
176  }
177 
178  prc_cat->warning()
179  << "changing description for ConfigVariable "
180  << get_name() << ".\n";
181  }
182 
183  _description = description;
184 }
185 
186 /**
187  * Specifies the default value for this variable if it is not defined in any
188  * prc file.
189  */
191 set_default_value(const string &default_value) {
192  if (_default_value == nullptr) {
193  // Defining the default value for the first time.
194  ConfigPage *default_page = ConfigPage::get_default_page();
195  _default_value = default_page->make_declaration(this, default_value);
196 
197  } else {
198  // Modifying an existing default value.
199 
200  // We set the original default value first, to avoid infinite recursion
201  // when the config variable in question happens to be consulted in
202  // NotifyCategory::out() (for instance, notify-timestamp).
203  string orig_default_value = _default_value->get_string_value();
204  _default_value->set_string_value(default_value);
205 
206  if (orig_default_value != default_value) {
207  if ((_flags & F_dconfig) != 0) {
208  // As a special exception, if the flags include F_dconfig, we don't
209  // report a warning for changing the default value, assuming the
210  // variable is being defined through the older DConfig interface.
211 
212  } else {
213  prc_cat->warning()
214  << "changing default value for ConfigVariable "
215  << get_name() << " from '" << orig_default_value
216  << "' to '" << default_value << "'.\n";
217  }
218  }
219  }
220 }
221 
222 /**
223  * Creates a new local value for this variable, if there is not already one
224  * specified. This will shadow any values defined in the various .prc files.
225  *
226  * If there is already a local value defined for this variable, simply returns
227  * that one.
228  *
229  * Use clear_local_value() to remove the local value definition.
230  */
233  if (_local_value == nullptr) {
234  ConfigPage *local_page = ConfigPage::get_local_page();
235  string string_value = get_declaration(0)->get_string_value();
236  _local_value = local_page->make_declaration(this, string_value);
237 
238  if (is_closed()) {
239  prc_cat.warning()
240  << "Assigning a local value to a \"closed\" ConfigVariable. "
241  "This is legal in a development build, but illegal in a release "
242  "build and may result in a compilation error or exception.\n";
243  }
244  }
245 
246  return _local_value;
247 }
248 
249 /**
250  * Removes the local value defined for this variable, and allows its value to
251  * be once again retrieved from the .prc files.
252  *
253  * Returns true if the value was successfully removed, false if it did not
254  * exist in the first place.
255  */
258  if (_local_value != nullptr) {
260  _local_value = nullptr;
261  invalidate_cache();
262  return true;
263  }
264 
265  return false;
266 }
267 
268 /**
269  * Returns true if this variable has an explicit value, either from a prc file
270  * or locally set, or false if variable has its default value.
271  */
273 has_value() const {
274  if (has_local_value()) {
275  return true;
276  }
277  check_sort_declarations();
278  return (!_trusted_declarations.empty());
279 }
280 
281 /**
282  * Returns the number of declarations that contribute to this variable's
283  * value. If the variable has been defined, this will always be at least 1
284  * (for the default value, at least).
285  */
286 size_t ConfigVariableCore::
287 get_num_declarations() const {
288  if (has_local_value()) {
289  return 1;
290  }
291  check_sort_declarations();
292  if (!_trusted_declarations.empty()) {
293  return _trusted_declarations.size();
294  }
295 
296  // We always have at least one: the default value.
297  return 1;
298 }
299 
300 /**
301  * Returns the nth declarations that contributes to this variable's value.
302  * The declarations are arranged in order such that earlier declarations
303  * shadow later declarations; thus, get_declaration(0) is always defined and
304  * always returns the current value of the variable.
305  */
307 get_declaration(size_t n) const {
308  ((ConfigVariableCore *)this)->_value_queried = true;
309  if (_default_value == nullptr) {
310  prc_cat->warning()
311  << "value queried before default value set for "
312  << get_name() << ".\n";
313  ((ConfigVariableCore *)this)->set_default_value("");
314  }
315 
316  if (has_local_value()) {
317  return _local_value;
318  }
319  check_sort_declarations();
320  if (n < _trusted_declarations.size()) {
321  return _trusted_declarations[n];
322  }
323  return _default_value;
324 }
325 
326 /**
327  *
328  */
329 void ConfigVariableCore::
330 output(std::ostream &out) const {
331  out << get_declaration(0)->get_string_value();
332 }
333 
334 /**
335  *
336  */
337 void ConfigVariableCore::
338 write(std::ostream &out) const {
339  out << "ConfigVariable " << get_name() << ":\n";
340 
341  check_sort_declarations();
342 
343  if (has_local_value()) {
344  out << " " << *_local_value << " (defined locally)\n";
345  }
346 
347  Declarations::const_iterator di;
348  for (di = _trusted_declarations.begin();
349  di != _trusted_declarations.end();
350  ++di) {
351  out << " " << *(*di)
352  << " (from " << (*di)->get_page()->get_name() << ")\n";
353  }
354 
355  if (_default_value != nullptr) {
356  out << " " << *_default_value << " (default value)\n";
357  }
358 
359  for (di = _untrusted_declarations.begin();
360  di != _untrusted_declarations.end();
361  ++di) {
362  out << " " << *(*di)
363  << " (from " << (*di)->get_page()->get_name() << ", untrusted)\n";
364  }
365 
366  if (!_description.empty()) {
367  out << "\n" << _description << "\n";
368  }
369 }
370 
371 /**
372  * Called only by the ConfigDeclaration constructor, this adds the indicated
373  * declaration to the list of declarations that reference this variable.
374  */
375 void ConfigVariableCore::
376 add_declaration(ConfigDeclaration *decl) {
377  _declarations.push_back(decl);
378 
379  _declarations_sorted = false;
380 }
381 
382 /**
383  * Called only by the ConfigDeclaration destructor, this removes the indicated
384  * declaration from the list of declarations that reference this variable.
385  */
386 void ConfigVariableCore::
387 remove_declaration(ConfigDeclaration *decl) {
388  Declarations::iterator di;
389  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
390  if ((*di) == decl) {
391  // Rather than deleting the declaration from the middle of the list, we
392  // maybe save a bit of time by swapping in the one at the end of the
393  // list (although this will unsort the list).
394  Declarations::iterator di2 = _declarations.end();
395  di2--;
396  (*di) = (*di2);
397  _declarations.erase(di2);
398  _declarations_sorted = false;
399  return;
400  }
401  }
402 
403  // Hmm, it wasn't here. Oh well.
404 }
405 
406 // This class is used in sort_declarations, below.
407 class CompareConfigDeclarations {
408 public:
409  bool operator () (const ConfigDeclaration *a, const ConfigDeclaration *b) const {
410  return (*a) < (*b);
411  }
412 };
413 
414 /**
415  * Sorts the list of declarations into priority order, so that the declaration
416  * at the front of the list is the one that shadows all following
417  * declarations.
418  */
419 void ConfigVariableCore::
420 sort_declarations() {
421  sort(_declarations.begin(), _declarations.end(), CompareConfigDeclarations());
422  Declarations::iterator di;
423 
424  // Now that they're sorted, divide them into either trusted or untrusted
425  // declarations.
426 #ifdef PRC_RESPECT_TRUST_LEVEL
427  // In this mode, normally for a release build, we sort the declarations
428  // honestly according to whether the prc file that defines them meets the
429  // required trust level.
430  _trusted_declarations.clear();
431  _untrusted_declarations.clear();
432  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
433  const ConfigDeclaration *decl = (*di);
434  if (!is_closed() &&
435  get_trust_level() <= decl->get_page()->get_trust_level()) {
436  _trusted_declarations.push_back(decl);
437  } else {
438  _untrusted_declarations.push_back(decl);
439  }
440  }
441 
442 #else // PRC_RESPECT_TRUST_LEVEL
443  // In this mode, normally for the development environment, all declarations
444  // are trusted, regardless of the trust level.
445  _trusted_declarations = _declarations;
446  _untrusted_declarations.clear();
447 
448 #endif // PRC_RESPECT_TRUST_LEVEL
449 
450  // Finally, determine the set of unique, trusted declarations--trusted
451  // declarations that have a unique string value. This is usually unneeded,
452  // but what the heck, it doesn't need to be recomputed all that often.
453  _unique_declarations.clear();
454 
455  init_system_type_handles(); // Make sure pset_type_handle is initted.
456  pset<string> already_added;
457  for (di = _trusted_declarations.begin();
458  di != _trusted_declarations.end();
459  ++di) {
460  const ConfigDeclaration *decl = (*di);
461  if (already_added.insert(decl->get_string_value()).second) {
462  _unique_declarations.push_back(decl);
463  }
464  }
465 
466  _declarations_sorted = true;
467 }
const std::string & get_string_value() const
Returns the value assigned to this variable.
The internal definition of a ConfigVariable.
set_description
Specifies the one-line description of this variable.
static ConfigPage * get_local_page()
Returns a pointer to the global "local page".
Definition: configPage.cxx:77
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_page
Returns the page on which this declaration can be found.
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set,...
static ConfigPage * get_default_page()
Returns a pointer to the global "default page".
Definition: configPage.cxx:64
get_trust_level
Returns the minimum trust_level a prc file must demonstrate in order to redefine the value for this v...
bool has_local_value() const
Returns true if this variable's value has been shadowed by a local assignment (as created via make_lo...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_default_value
Specifies the default value for this variable if it is not defined in any prc file.
is_closed
Returns true if the variable is not trusted by any prc file (and hence cannot be modified from its co...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool clear_local_value()
Removes the local value defined for this variable, and allows its value to be once again retrieved fr...
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
void set_string_value(const std::string &value)
Changes the value assigned to this variable.
void set_flags(int flags)
Specifies the trust level of this variable.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_name
Returns the name of the variable.
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
get_declaration
Returns the nth declarations that contributes to this variable's value.
set_value_type
Specifies the type of this variable.
ConfigDeclaration * make_local_value()
Creates a new local value for this variable, if there is not already one specified.
A single declaration of a config variable, typically defined as one line in a .prc file,...
ConfigDeclaration * make_declaration(const std::string &variable, const std::string &value)
Adds the indicated variable/value pair as a new declaration on the page.
Definition: configPage.cxx:237
bool delete_declaration(ConfigDeclaration *decl)
Removes the indicated declaration from the page and deletes it.
Definition: configPage.cxx:263