468 lines
14 KiB
C++
468 lines
14 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file configVariableCore.cxx
|
|
* @author drose
|
|
* @date 2004-10-15
|
|
*/
|
|
|
|
#include "configVariableCore.h"
|
|
#include "configDeclaration.h"
|
|
#include "configPage.h"
|
|
#include "pset.h"
|
|
#include "pnotify.h"
|
|
#include "config_prc.h"
|
|
|
|
// This file is generated by ppremake.
|
|
#include "prc_parameters.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using std::string;
|
|
|
|
|
|
/**
|
|
* Use the ConfigVariableManager::make_variable() interface to create a new
|
|
* ConfigVariableCore.
|
|
*/
|
|
ConfigVariableCore::
|
|
ConfigVariableCore(const string &name) :
|
|
_name(name),
|
|
_is_used(false),
|
|
_value_type(VT_undefined),
|
|
_flags(0),
|
|
_default_value(nullptr),
|
|
_local_value(nullptr),
|
|
_declarations_sorted(true),
|
|
_value_queried(false)
|
|
{
|
|
#if defined(PRC_INC_TRUST_LEVEL) && PRC_INC_TRUST_LEVEL != 0
|
|
_flags = (_flags & ~F_trust_level_mask) | ((_flags & F_trust_level_mask) + PRC_INC_TRUST_LEVEL);
|
|
#endif // PRC_INC_TRUST_LEVEL
|
|
}
|
|
|
|
/**
|
|
* This is used by ConfigVariableManager to create the variable from a
|
|
* template--basically, another variable with all of the initial properties
|
|
* pre-defined.
|
|
*/
|
|
ConfigVariableCore::
|
|
ConfigVariableCore(const ConfigVariableCore &templ, const string &name) :
|
|
_name(name),
|
|
_is_used(templ._is_used),
|
|
_value_type(templ._value_type),
|
|
_description(templ._description),
|
|
_flags(templ._flags),
|
|
_default_value(nullptr),
|
|
_local_value(nullptr),
|
|
_declarations_sorted(false),
|
|
_value_queried(false)
|
|
{
|
|
if (templ._default_value != nullptr) {
|
|
set_default_value(templ._default_value->get_string_value());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The destructor should never be called; ConfigVariableCore objects live
|
|
* forever and never get destructed.
|
|
*/
|
|
ConfigVariableCore::
|
|
~ConfigVariableCore() {
|
|
prc_cat->error()
|
|
<< "Internal error--ConfigVariableCore destructor called!\n";
|
|
}
|
|
|
|
/**
|
|
* Specifies the type of this variable. See get_value_type(). It is not an
|
|
* error to call this multiple times, but if the value changes once
|
|
* get_declaration() has been called, a warning is printed.
|
|
*/
|
|
void ConfigVariableCore::
|
|
set_value_type(ConfigVariableCore::ValueType value_type) {
|
|
if (_value_queried && _value_type != value_type) {
|
|
if ((_flags & F_dconfig) != 0) {
|
|
// As a special exception, if the flags include F_dconfig, we don't
|
|
// report a warning for changing the type, assuming the variable is
|
|
// being defined through the older DConfig interface.
|
|
|
|
} else {
|
|
prc_cat->warning()
|
|
<< "changing type for ConfigVariable "
|
|
<< get_name() << " from " << _value_type << " to "
|
|
<< value_type << ".\n";
|
|
}
|
|
}
|
|
|
|
_value_type = value_type;
|
|
}
|
|
|
|
/**
|
|
* Specifies the trust level of this variable. See get_flags(). It is not an
|
|
* error to call this multiple times, but if the value changes once
|
|
* get_declaration() has been called, a warning is printed.
|
|
*/
|
|
void ConfigVariableCore::
|
|
set_flags(int flags) {
|
|
if ((flags & F_dconfig) != 0) {
|
|
flags = (flags & ~F_trust_level_mask) | PRC_DCONFIG_TRUST_LEVEL;
|
|
}
|
|
|
|
#if defined(PRC_INC_TRUST_LEVEL) && PRC_INC_TRUST_LEVEL != 0
|
|
flags = (flags & ~F_trust_level_mask) | ((flags & F_trust_level_mask) + PRC_INC_TRUST_LEVEL);
|
|
#endif // PRC_INC_TRUST_LEVEL
|
|
|
|
if (_value_queried) {
|
|
int bits_changed = (_flags ^ flags);
|
|
if ((bits_changed & F_trust_level_mask) != 0) {
|
|
prc_cat->warning()
|
|
<< "changing trust level for ConfigVariable "
|
|
<< get_name() << " from " << (_flags & F_trust_level_mask) << " to "
|
|
<< (flags & F_trust_level_mask) << ".\n";
|
|
}
|
|
if ((bits_changed & ~(F_trust_level_mask | F_dconfig)) != 0) {
|
|
prc_cat->warning()
|
|
<< "changing flags for ConfigVariable "
|
|
<< get_name() << " from " << std::hex
|
|
<< (_flags & ~F_trust_level_mask) << " to "
|
|
<< (flags & ~F_trust_level_mask) << std::dec << ".\n";
|
|
}
|
|
}
|
|
|
|
_flags = flags;
|
|
|
|
// Changing the trust level will require re-sorting the declarations.
|
|
_declarations_sorted = false;
|
|
}
|
|
|
|
/**
|
|
* Specifies the one-line description of this variable. See
|
|
* get_description(). It is not an error to call this multiple times, but if
|
|
* the value changes once get_declaration() has been called, a warning is
|
|
* printed.
|
|
*/
|
|
void ConfigVariableCore::
|
|
set_description(const string &description) {
|
|
if (_value_queried && _description != description) {
|
|
if ((_flags & F_dconfig) != 0) {
|
|
// As a special exception, if the flags include F_dconfig, we don't
|
|
// change it, since this is presumably coming from the older DConfig
|
|
// interface.
|
|
return;
|
|
}
|
|
if (description == "DConfig") {
|
|
// As a similar exception, we don't replace an existing description with
|
|
// one that reads simply "DConfig", unless it was empty previously.
|
|
if (_description.empty()) {
|
|
_description = description;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (description.empty()) {
|
|
// If the new description is empty, we don't do anything.
|
|
return;
|
|
}
|
|
|
|
if (_description.empty()) {
|
|
// If the previous description was empty, we quietly replace it.
|
|
_description = description;
|
|
return;
|
|
}
|
|
|
|
prc_cat->warning()
|
|
<< "changing description for ConfigVariable "
|
|
<< get_name() << ".\n";
|
|
}
|
|
|
|
_description = description;
|
|
}
|
|
|
|
/**
|
|
* Specifies the default value for this variable if it is not defined in any
|
|
* prc file.
|
|
*/
|
|
void ConfigVariableCore::
|
|
set_default_value(const string &default_value) {
|
|
if (_default_value == nullptr) {
|
|
// Defining the default value for the first time.
|
|
ConfigPage *default_page = ConfigPage::get_default_page();
|
|
_default_value = default_page->make_declaration(this, default_value);
|
|
|
|
} else {
|
|
// Modifying an existing default value.
|
|
|
|
// We set the original default value first, to avoid infinite recursion
|
|
// when the config variable in question happens to be consulted in
|
|
// NotifyCategory::out() (for instance, notify-timestamp).
|
|
string orig_default_value = _default_value->get_string_value();
|
|
_default_value->set_string_value(default_value);
|
|
|
|
if (orig_default_value != default_value) {
|
|
if ((_flags & F_dconfig) != 0) {
|
|
// As a special exception, if the flags include F_dconfig, we don't
|
|
// report a warning for changing the default value, assuming the
|
|
// variable is being defined through the older DConfig interface.
|
|
|
|
} else {
|
|
prc_cat->warning()
|
|
<< "changing default value for ConfigVariable "
|
|
<< get_name() << " from '" << orig_default_value
|
|
<< "' to '" << default_value << "'.\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new local value for this variable, if there is not already one
|
|
* specified. This will shadow any values defined in the various .prc files.
|
|
*
|
|
* If there is already a local value defined for this variable, simply returns
|
|
* that one.
|
|
*
|
|
* Use clear_local_value() to remove the local value definition.
|
|
*/
|
|
ConfigDeclaration *ConfigVariableCore::
|
|
make_local_value() {
|
|
if (_local_value == nullptr) {
|
|
ConfigPage *local_page = ConfigPage::get_local_page();
|
|
string string_value = get_declaration(0)->get_string_value();
|
|
_local_value = local_page->make_declaration(this, string_value);
|
|
|
|
if (is_closed()) {
|
|
prc_cat.warning()
|
|
<< "Assigning a local value to a \"closed\" ConfigVariable. "
|
|
"This is legal in a development build, but illegal in a release "
|
|
"build and may result in a compilation error or exception.\n";
|
|
}
|
|
}
|
|
|
|
return _local_value;
|
|
}
|
|
|
|
/**
|
|
* Removes the local value defined for this variable, and allows its value to
|
|
* be once again retrieved from the .prc files.
|
|
*
|
|
* Returns true if the value was successfully removed, false if it did not
|
|
* exist in the first place.
|
|
*/
|
|
bool ConfigVariableCore::
|
|
clear_local_value() {
|
|
if (_local_value != nullptr) {
|
|
ConfigPage::get_local_page()->delete_declaration(_local_value);
|
|
_local_value = nullptr;
|
|
invalidate_cache();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this variable has an explicit value, either from a prc file
|
|
* or locally set, or false if variable has its default value.
|
|
*/
|
|
bool ConfigVariableCore::
|
|
has_value() const {
|
|
if (has_local_value()) {
|
|
return true;
|
|
}
|
|
check_sort_declarations();
|
|
return (!_trusted_declarations.empty());
|
|
}
|
|
|
|
/**
|
|
* Returns the number of declarations that contribute to this variable's
|
|
* value. If the variable has been defined, this will always be at least 1
|
|
* (for the default value, at least).
|
|
*/
|
|
size_t ConfigVariableCore::
|
|
get_num_declarations() const {
|
|
if (has_local_value()) {
|
|
return 1;
|
|
}
|
|
check_sort_declarations();
|
|
if (!_trusted_declarations.empty()) {
|
|
return _trusted_declarations.size();
|
|
}
|
|
|
|
// We always have at least one: the default value.
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Returns the nth declarations that contributes to this variable's value.
|
|
* The declarations are arranged in order such that earlier declarations
|
|
* shadow later declarations; thus, get_declaration(0) is always defined and
|
|
* always returns the current value of the variable.
|
|
*/
|
|
const ConfigDeclaration *ConfigVariableCore::
|
|
get_declaration(size_t n) const {
|
|
((ConfigVariableCore *)this)->_value_queried = true;
|
|
if (_default_value == nullptr) {
|
|
prc_cat->warning()
|
|
<< "value queried before default value set for "
|
|
<< get_name() << ".\n";
|
|
((ConfigVariableCore *)this)->set_default_value("");
|
|
}
|
|
|
|
if (has_local_value()) {
|
|
return _local_value;
|
|
}
|
|
check_sort_declarations();
|
|
if (n < _trusted_declarations.size()) {
|
|
return _trusted_declarations[n];
|
|
}
|
|
return _default_value;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void ConfigVariableCore::
|
|
output(std::ostream &out) const {
|
|
out << get_declaration(0)->get_string_value();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void ConfigVariableCore::
|
|
write(std::ostream &out) const {
|
|
out << "ConfigVariable " << get_name() << ":\n";
|
|
|
|
check_sort_declarations();
|
|
|
|
if (has_local_value()) {
|
|
out << " " << *_local_value << " (defined locally)\n";
|
|
}
|
|
|
|
Declarations::const_iterator di;
|
|
for (di = _trusted_declarations.begin();
|
|
di != _trusted_declarations.end();
|
|
++di) {
|
|
out << " " << *(*di)
|
|
<< " (from " << (*di)->get_page()->get_name() << ")\n";
|
|
}
|
|
|
|
if (_default_value != nullptr) {
|
|
out << " " << *_default_value << " (default value)\n";
|
|
}
|
|
|
|
for (di = _untrusted_declarations.begin();
|
|
di != _untrusted_declarations.end();
|
|
++di) {
|
|
out << " " << *(*di)
|
|
<< " (from " << (*di)->get_page()->get_name() << ", untrusted)\n";
|
|
}
|
|
|
|
if (!_description.empty()) {
|
|
out << "\n" << _description << "\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called only by the ConfigDeclaration constructor, this adds the indicated
|
|
* declaration to the list of declarations that reference this variable.
|
|
*/
|
|
void ConfigVariableCore::
|
|
add_declaration(ConfigDeclaration *decl) {
|
|
_declarations.push_back(decl);
|
|
|
|
_declarations_sorted = false;
|
|
}
|
|
|
|
/**
|
|
* Called only by the ConfigDeclaration destructor, this removes the indicated
|
|
* declaration from the list of declarations that reference this variable.
|
|
*/
|
|
void ConfigVariableCore::
|
|
remove_declaration(ConfigDeclaration *decl) {
|
|
Declarations::iterator di;
|
|
for (di = _declarations.begin(); di != _declarations.end(); ++di) {
|
|
if ((*di) == decl) {
|
|
// Rather than deleting the declaration from the middle of the list, we
|
|
// maybe save a bit of time by swapping in the one at the end of the
|
|
// list (although this will unsort the list).
|
|
Declarations::iterator di2 = _declarations.end();
|
|
di2--;
|
|
(*di) = (*di2);
|
|
_declarations.erase(di2);
|
|
_declarations_sorted = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Hmm, it wasn't here. Oh well.
|
|
}
|
|
|
|
// This class is used in sort_declarations, below.
|
|
class CompareConfigDeclarations {
|
|
public:
|
|
bool operator () (const ConfigDeclaration *a, const ConfigDeclaration *b) const {
|
|
return (*a) < (*b);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sorts the list of declarations into priority order, so that the declaration
|
|
* at the front of the list is the one that shadows all following
|
|
* declarations.
|
|
*/
|
|
void ConfigVariableCore::
|
|
sort_declarations() {
|
|
sort(_declarations.begin(), _declarations.end(), CompareConfigDeclarations());
|
|
Declarations::iterator di;
|
|
|
|
// Now that they're sorted, divide them into either trusted or untrusted
|
|
// declarations.
|
|
#ifdef PRC_RESPECT_TRUST_LEVEL
|
|
// In this mode, normally for a release build, we sort the declarations
|
|
// honestly according to whether the prc file that defines them meets the
|
|
// required trust level.
|
|
_trusted_declarations.clear();
|
|
_untrusted_declarations.clear();
|
|
for (di = _declarations.begin(); di != _declarations.end(); ++di) {
|
|
const ConfigDeclaration *decl = (*di);
|
|
if (!is_closed() &&
|
|
get_trust_level() <= decl->get_page()->get_trust_level()) {
|
|
_trusted_declarations.push_back(decl);
|
|
} else {
|
|
_untrusted_declarations.push_back(decl);
|
|
}
|
|
}
|
|
|
|
#else // PRC_RESPECT_TRUST_LEVEL
|
|
// In this mode, normally for the development environment, all declarations
|
|
// are trusted, regardless of the trust level.
|
|
_trusted_declarations = _declarations;
|
|
_untrusted_declarations.clear();
|
|
|
|
#endif // PRC_RESPECT_TRUST_LEVEL
|
|
|
|
// Finally, determine the set of unique, trusted declarations--trusted
|
|
// declarations that have a unique string value. This is usually unneeded,
|
|
// but what the heck, it doesn't need to be recomputed all that often.
|
|
_unique_declarations.clear();
|
|
|
|
init_system_type_handles(); // Make sure pset_type_handle is initted.
|
|
pset<string> already_added;
|
|
for (di = _trusted_declarations.begin();
|
|
di != _trusted_declarations.end();
|
|
++di) {
|
|
const ConfigDeclaration *decl = (*di);
|
|
if (already_added.insert(decl->get_string_value()).second) {
|
|
_unique_declarations.push_back(decl);
|
|
}
|
|
}
|
|
|
|
_declarations_sorted = true;
|
|
}
|