open_toontown_panda3d/dtool/src/prc/configVariableCore.cxx

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;
}