panda3d/dtool/src/prc/configVariableCore.cxx
2007-06-28 04:12:14 +00:00

507 lines
18 KiB
C++

// Filename: configVariableCore.cxx
// Created by: drose (15Oct04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#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>
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::Constructor
// Access: Private
// Description: 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(NULL),
_local_value(NULL),
_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
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::Copy Constructor
// Access: Private
// Description: 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(NULL),
_local_value(NULL),
_declarations_sorted(false),
_value_queried(false)
{
if (templ._default_value != (ConfigDeclaration *)NULL) {
set_default_value(templ._default_value->get_string_value());
}
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::Destructor
// Access: Private
// Description: 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";
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::set_value_type
// Access: Public
// Description: 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;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::set_flags
// Access: Public
// Description: 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 " << hex
<< (_flags & ~F_trust_level_mask) << " to "
<< (flags & ~F_trust_level_mask) << dec << ".\n";
}
}
_flags = flags;
// Changing the trust level will require re-sorting the
// declarations.
_declarations_sorted = false;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::set_description
// Access: Public
// Description: 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;
}
prc_cat->warning()
<< "changing description for ConfigVariable "
<< get_name() << ".\n";
}
_description = description;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::set_default_value
// Access: Public
// 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 == (ConfigDeclaration *)NULL) {
// 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.
if (_default_value->get_string_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 '" << _default_value->get_string_value()
<< "' to '" << default_value << "'.\n";
}
_default_value->set_string_value(default_value);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::make_local_value
// Access: Public
// Description: 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 == (ConfigDeclaration *)NULL) {
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;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::clear_local_value
// Access: Public
// Description: 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 != (ConfigDeclaration *)NULL) {
ConfigPage::get_local_page()->delete_declaration(_local_value);
_local_value = (ConfigDeclaration *)NULL;
invalidate_cache();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::has_value
// Access: Public
// Description: 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());
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::get_num_declarations
// Access: Public
// Description: 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).
////////////////////////////////////////////////////////////////////
int 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;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::get_declaration
// Access: Public
// Description: 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(int n) const {
((ConfigVariableCore *)this)->_value_queried = true;
if (_default_value == (ConfigDeclaration *)NULL) {
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 >= 0 && n < (int)_trusted_declarations.size()) {
return _trusted_declarations[n];
}
return _default_value;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::output
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ConfigVariableCore::
output(ostream &out) const {
out << get_declaration(0)->get_string_value();
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ConfigVariableCore::
write(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 != (ConfigDeclaration *)NULL) {
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";
}
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::add_declaration
// Access: Private
// Description: 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;
}
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::remove_declaration
// Access: Private
// Description: 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);
}
};
////////////////////////////////////////////////////////////////////
// Function: ConfigVariableCore::sort_declarations
// Access: Private
// Description: 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;
}