DYT/Tool/OpenSceneGraph-3.6.5/include/osgEarth/Config
2024-12-25 07:49:36 +08:00

792 lines
27 KiB
C++

/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2020 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#pragma once
#include <osgEarth/Common>
#include <osgEarth/optional>
#include <osgEarth/StringUtils>
#include <osg/Vec2f>
#include <osg/Vec3d>
#include <vector>
#include <istream>
#include <functional>
namespace osgDB
{
class Options;
}
#define OE_CONFIG_MOVE_SEMATICS
namespace osgEarth
{
using namespace osgEarth::Util;
using ConfigSet = std::vector<class Config>;
class URI;
/**
* Config is a general-purpose container for serializable data. You store an object's members
* to Config, and then translate the Config to a particular format (like XML or JSON). Likewise,
* the object can de-serialize a Config back into member data. Config support the optional<>
* template for optional values.
*/
class OSGEARTH_EXPORT Config
{
protected:
std::string _key;
std::string _defaultValue;
std::string _referrer;
std::string _externalRef;
ConfigSet _children;
bool _isLocation = false;
bool _isNumber = false;
public:
Config() = default;
Config(const std::string& key) :
_key(key) { }
template<typename T>
explicit Config(const std::string& key, const T& value) :
_key(key)
{
setValue(value);
}
explicit Config(const std::string& key, const Config& value) :
_key(key)
{
add(value);
}
#ifdef OE_CONFIG_MOVE_SEMATICS
explicit Config(const std::string& key, Config&& value) :
_key(key)
{
add(value);
}
Config(Config&& rhs) = default;
Config& operator=(Config&& rhs) = default;
#endif
// Copy functions
Config(const Config& rhs) = default;
Config& operator=(const Config& rhs) = default;
/**
* Referrer is the context for resolving relative pathnames that occur in this object.
* For example, if the value is a filename "file.txt" and the referrer is "C:/temp/a.earth",
* then the full path of the file is "C:/temp/file.txt".
*
* Calling this sets a referrer on this object and its children.
*/
void setReferrer(const std::string& value);
/** Access this object's "relative-to" location. */
const std::string& referrer() const { return _referrer; }
/** Referrer associated with a key */
const std::string referrer(const std::string& key) const {
return child(key).referrer();
}
/** Sets whether this Config's value represents a location, i.e. a URI, filename, or
other string that can be relocated to be relative to a different referrer. */
void setIsLocation(bool tf) { _isLocation = tf; }
bool isLocation() const { return _isLocation; }
/** Hint that this Config came from an externally referenced resource. */
const std::string& externalRef() const { return _externalRef; }
void setExternalRef(const std::string& externalRef) { _externalRef = externalRef; }
/** Populate this object from an XML input stream. */
bool fromXML(std::istream& in);
//! Populate this object from a URI
bool fromURI(const URI&);
/** Encode this object as JSON. */
std::string toJSON(bool pretty = false) const;
/** Populate this object from a JSON string. */
bool fromJSON(const std::string& json);
static Config readJSON(const std::string& json);
/** True if this object contains no data. */
bool empty() const {
return _key.empty() && _defaultValue.empty() && _children.empty();
}
/** True is this object is a simple key/value pair with no children. */
bool isSimple() const {
return !_key.empty() && !_defaultValue.empty() && _children.empty();
}
/** The key value for this object */
std::string& key() { return _key; }
const std::string& key() const { return _key; }
/** The value corresponding to the key */
const std::string& value() const { return _defaultValue; }
/** Main setValue method - see specializations inline below */
template<typename T>
void setValue(const T& value) {
_defaultValue = Stringify() << value;
}
/** Child objects. */
ConfigSet& children() { return _children; }
const ConfigSet& children() const { return _children; }
/** A collection of all the children of this object with a particular key */
const ConfigSet children(const std::string& key) const {
ConfigSet r;
for (ConfigSet::const_iterator i = _children.begin(); i != _children.end(); i++) {
if (i->key() == key)
r.push_back(*i);
}
return r;
}
/** Whether this object has a child with a given key */
bool hasChild(const std::string& key) const {
for (ConfigSet::const_iterator i = _children.begin(); i != _children.end(); i++)
if (i->key() == key)
return true;
return false;
}
/** Removes all children with the given key */
void remove(const std::string& key) {
for (ConfigSet::iterator i = _children.begin(); i != _children.end(); ) {
if (i->key() == key)
i = _children.erase(i);
else
++i;
}
}
/** First child with the given key */
const Config& child(const std::string& key) const;
/** Pointer to the first child with the given key, or NULL if none exist */
const Config* child_ptr(const std::string& key) const;
/** Mutable pointer to the first child with the given key, or NULL if none exist */
Config* mutable_child(const std::string& key);
/** Merge the contents of another Config object into this object.. danger, read the code
before you use this */
void merge(const Config& rhs);
/** Locate (recursively) the first descendant object with this key, optionally checking
the current object as well */
Config* find(const std::string& key, bool checkThis = true);
const Config* find(const std::string& key, bool checkThis = true) const;
/** Add a value as a child */
template<typename T>
Config& add(const std::string& key, const T& value) {
_children.push_back(Config(key, value));
_children.back().setReferrer(_referrer);
return _children.back();
}
//! Set a config as a child
Config& add(const Config& conf) {
_children.push_back(conf);
_children.back().setReferrer(_referrer);
return _children.back();
}
/** Add a config as a child, assigning it a key */
Config& add(const std::string& key, const Config& conf) {
Config& result = add(conf);
result.key() = key;
return result;
}
//! Add a config named key
Config& add(const std::string& key) {
return add(Config(key));
}
/** Add a set of config objects as children. */
void add(const ConfigSet& set) {
for (ConfigSet::const_iterator i = set.begin(); i != set.end(); i++)
add(*i);
}
#ifdef OE_CONFIG_MOVE_SEMATICS
//! Move a config as a child
Config& add(Config&& conf) {
_children.push_back(std::move(conf));
_children.back().setReferrer(_referrer);
conf = {};
return _children.back();
}
Config& add(const std::string& key, Config&& conf) {
Config& result = add(std::move(conf));
result.key() = key;
conf = {};
return result;
}
Config& set(Config&& conf) {
remove(conf.key());
return add(std::move(conf));
}
#endif
/** Adds or replaces an optional value as a child, but only if it is set */
template<typename T>
void set(const std::string& key, const optional<T>& opt) {
remove(key);
if (opt.isSet()) {
set(Config(key, opt.get()));
}
}
template<typename T>
void set(const std::string& key, const osg::ref_ptr<T>& obj) {
remove(key);
if (obj.valid()) {
Config conf = obj->getConfig();
conf.key() = key;
set(std::move(conf));
}
}
// If target is set to targetValue, set key to val.
template<typename X, typename Y>
void set(const std::string& key, const std::string& val, const optional<X>& target, const Y& targetValue) {
if (target.isSetTo(targetValue)) {
remove(key);
set(key, val);
}
}
/** Adds or replaces a config as a child. */
Config& set(const Config& conf) {
remove(conf.key());
return add(conf);
}
//! Adds or replaces a config as a child if it's not empty.
template<typename CALLABLE>
void set_with_function(const std::string& key, CALLABLE&& f) {
Config conf(key);
f(conf);
if (!conf.empty()) set(std::move(conf));
}
/** Sets a key value pair child */
template<typename T>
void set(const std::string& key, const T& value) {
set(Config(key, value));
}
/** Whether this object has the key OR has a child with the key */
bool hasValue(const std::string& key) const {
return !value(key).empty();
}
/** The value of this object (if the key matches) or a matching child object */
const std::string value(const std::string& key) const {
std::string r = trim(child(key).value());
if (r.empty() && _key == key)
r = _defaultValue;
return r;
}
/** Default value transformed to another type */
template<typename T>
T valueAs(const T& fallback) const {
return osgEarth::Util::as<T>(_defaultValue, fallback);
}
/** Value cast to a particular primitive type (with fallback in case casting fails) */
template<typename T>
T value(const std::string& key, T fallback) const {
std::string r;
if (hasChild(key))
r = child(key).value();
return osgEarth::Util::as<T>(r, fallback);
}
/** Populates the output value iff the Config exists. */
template<typename T>
bool get(const std::string& key, optional<T>& output) const {
std::string r;
if (hasChild(key))
r = child(key).value();
if (!r.empty()) {
output = osgEarth::Util::as<T>(r, output.defaultValue());
return true;
}
else
return false;
}
/** Populates the output referenced value iff the Config exists. */
template<typename T>
bool get(const std::string& key, osg::ref_ptr<T>& output) const {
if (hasChild(key)) {
output = new T(child(key));
return true;
}
else
return false;
}
/** Populates the output enumerable pair iff the Config exists. */
template<typename X, typename Y>
bool get(const std::string& key, const std::string& val, optional<X>& target, const Y& targetValue) const {
if (hasValue(key) && value(key) == val) {
target = targetValue;
return true;
}
else
return false;
}
/** Populates the output enumerable pair iff the Config exists. */
template<typename X, typename Y>
bool get(const std::string& key, const std::string& val, X& target, const Y& targetValue) const {
if (hasValue(key) && value(key) == val) {
target = targetValue;
return true;
}
return false;
}
/** Populates the ouptut value iff the Config exists. */
template<typename T>
bool get(const std::string& key, T& output) const {
if (hasValue(key)) {
output = value<T>(key, output);
return true;
}
return false;
}
// remove everything from (this) that also appears in rhs (diff)
Config operator - (const Config& rhs) const;
//! Whether the encoded value is actual a number
bool isNumber() const { return _isNumber; }
};
// SPECIALIZATION - Config
template<> inline
void Config::set<Config>(const std::string& key, const Config& conf) {
remove(key);
Config& result = add(conf);
result.key() = key;
}
template<> inline
void Config::set<Config>(const std::string& key, const optional<Config>& opt) {
remove(key);
if (opt.isSet()) {
Config& result = add(opt.value());
result.key() = key;
}
}
template<> inline
bool Config::get<Config>(const std::string& key, optional<Config>& output) const {
if (hasChild(key)) {
output = child(key);
return true;
}
else
return false;
}
// SPECIALIZATIONS - setValue
template<> inline void Config::setValue<std::string>(const std::string& value) {
_defaultValue = value;
_isNumber = false;
}
template<> inline void Config::setValue<bool>(const bool& value) {
_defaultValue = value==true? "true" : "false";
_isNumber = false;
}
template<> inline void Config::setValue<short>(const short& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<unsigned short>(const unsigned short& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<int>(const int& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<unsigned int>(const unsigned int& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<long>(const long& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<unsigned long>(const unsigned long& value) {
_defaultValue = std::to_string(value);
_isNumber = true;
}
template<> inline void Config::setValue<float>(const float& value) {
_defaultValue = Stringify() << std::setprecision(8) << value;
_isNumber = true;
}
template<> inline void Config::setValue<double>(const double& value) {
_defaultValue = Stringify() << std::setprecision(16) << value;
_isNumber = true;
}
// Specializations (OSG types)
template<> inline void Config::set<osg::Vec2f>(const std::string& key, const optional<osg::Vec2f>& opt) {
remove(key);
if (opt.isSet()) {
set(key, Stringify() << std::setprecision(8) << opt->x() << "," << opt->y());
}
}
template<> inline bool Config::get<osg::Vec2f>(const std::string& key, optional<osg::Vec2f>& output) const {
if (hasChild(key)) {
output.mutable_value().x() = as<float>(getToken(value(key), 0), 0.0f);
output.mutable_value().y() = as<float>(getToken(value(key), 1), 0.0f);
return true;
}
else
return false;
}
template<> inline void Config::set<osg::Vec2d>(const std::string& key, const optional<osg::Vec2d>& opt) {
remove(key);
if (opt.isSet()) {
set(key, Stringify() << std::setprecision(16) << opt->x() << "," << opt->y());
}
}
template<> inline bool Config::get<osg::Vec2d>(const std::string& key, optional<osg::Vec2d>& output) const {
if (hasChild(key)) {
output.mutable_value().x() = as<double>(getToken(value(key), 0), 0.0);
output.mutable_value().y() = as<double>(getToken(value(key), 1), 0.0);
return true;
}
else
return false;
}
template<> inline void Config::set<osg::Vec3f>(const std::string& key, const optional<osg::Vec3f>& opt) {
remove(key);
if (opt.isSet()) {
set(key, Stringify() << std::setprecision(8) << opt->x() << "," << opt->y() << "," << opt->z());
}
}
template<> inline bool Config::get<osg::Vec3f>(const std::string& key, optional<osg::Vec3f>& output) const {
if (hasChild(key)) {
output.mutable_value().x() = as<float>(getToken(value(key), 0), 0.0f);
output.mutable_value().y() = as<float>(getToken(value(key), 1), 0.0f);
output.mutable_value().z() = as<float>(getToken(value(key), 2), 0.0f);
return true;
}
else
return false;
}
template<> inline void Config::set<osg::Vec3d>(const std::string& key, const optional<osg::Vec3d>& opt) {
remove(key);
if (opt.isSet()) {
set(key, Stringify() << std::setprecision(16) << opt->x() << "," << opt->y() << "," << opt->z());
}
}
template<> inline bool Config::get<osg::Vec3d>(const std::string& key, optional<osg::Vec3d>& output) const {
if (hasChild(key)) {
output.mutable_value().x() = as<double>(getToken(value(key), 0), 0.0);
output.mutable_value().y() = as<double>(getToken(value(key), 1), 0.0);
output.mutable_value().z() = as<double>(getToken(value(key), 2), 0.0);
return true;
}
else
return false;
}
template<> inline void Config::set(const std::string& key, const std::vector<std::string>& input) {
remove(key);
if (!input.empty()) {
std::ostringstream buf;
for(size_t i=0; i<input.size(); ++i) {
if (i > 0) buf << ',';
bool quote = input[i].find(',') != std::string::npos;
if (quote) buf << "\"";
buf << input[i];
if (quote) buf << "\"";
}
set(key, buf.str());
}
}
template<> inline bool Config::get(const std::string& key, std::vector<std::string>& output) const {
if (hasChild(key)) {
output.clear();
std::vector<std::string> tokens;
StringTokenizer(value(key), output, ",", "\"", true, true);
return true;
}
else return false;
}
// Use this macro to "activate" any object with a getConfig/ctor(const Config&) pair
// and make it usable with Config::set/get/add.
// NOTE: You must only use this macro in the global namespace!
#define OSGEARTH_SPECIALIZE_CONFIG(TYPE) \
namespace osgEarth { \
template<> inline \
void Config::set<TYPE>(const std::string& key, const TYPE& obj) { \
set( key, obj.getConfig() ); \
} \
template<> inline \
void Config::set<TYPE>(const std::string& key, const optional<TYPE>& opt) { \
if ( opt.isSet() ) \
set(key, opt.get()); \
} \
template<> inline \
bool Config::get<TYPE>(const std::string& key, TYPE& opt) const { \
if ( hasChild(key) ) { \
opt = TYPE(child(key)); \
return true; \
} \
else return false; \
} \
template<> inline \
bool Config::get<TYPE>(const std::string& key, optional<TYPE>& opt) const { \
if ( hasChild(key) ) { \
opt = TYPE(child(key)); \
return true; \
} \
else return false; \
} \
template<> inline Config& Config::add<TYPE>(const std::string& key, const TYPE& value) { \
Config conf = value.getConfig(); \
conf.key() = key; \
return add(std::move(conf)); \
} \
} // namespace osgEarth
//--------------------------------------------------------------------
/**
* Base class for all serializable options classes.
*/
class ConfigOptions
{
public:
ConfigOptions() = default;
//! Copy constructor - it must call getConfig() so it can repopulate the internal _conf member.
ConfigOptions(const ConfigOptions& rhs) : _conf(rhs.getConfig()) { }
//! Copy constructor - simple Config copy.
ConfigOptions(const Config& conf) : _conf(conf) { }
const std::string& referrer() const {
return _conf.referrer();
}
ConfigOptions& operator = (const ConfigOptions& rhs) {
if (this != &rhs) {
_conf = rhs.getConfig();
mergeConfig(_conf);
}
return *this;
}
void merge(const ConfigOptions& rhs) {
_conf.merge(rhs._conf);
mergeConfig(rhs.getConfig());
}
virtual Config getConfig() const {
Config conf = _conf;
conf.setReferrer(referrer());
return conf;
}
bool empty() const {
return _conf.empty();
}
protected:
virtual void mergeConfig(const Config& conf) { }
Config _conf;
};
/**
* Base configoptions class for driver options.
* @deprecated - will be removed.
*/
class DriverConfigOptions : public ConfigOptions
{
public:
DriverConfigOptions(const ConfigOptions& rhs = ConfigOptions())
: ConfigOptions(rhs)
{
fromConfig(_conf);
}
/** Gets or sets the name of the driver to load */
void setDriver(const std::string& value) { _driver = value; }
const std::string& getDriver() const { return _driver; }
public:
virtual Config getConfig() const {
Config conf = ConfigOptions::getConfig();
if (!_driver.empty())
conf.set("driver", _driver);
return conf;
}
virtual void mergeConfig(const Config& conf) {
ConfigOptions::mergeConfig(conf);
fromConfig(conf);
}
public:
void fromConfig(const Config& conf) {
_driver = conf.value("driver");
if (_driver.empty() && conf.hasValue("type"))
_driver = conf.value("type");
}
private:
std::string _name, _driver;
};
}
//! Macro to use when defining a COnfigOptions class
#define META_ConfigOptions(LIBRARY, MYCLASS, SUPERCLASS) \
protected: \
virtual void mergeConfig(const Config& conf) { \
SUPERCLASS ::mergeConfig(conf); \
fromConfig(conf); \
} \
public: \
MYCLASS () : SUPERCLASS() { fromConfig(_conf); } \
MYCLASS (const ConfigOptions& opt) : SUPERCLASS(opt) { fromConfig(_conf); }
//! optional property macro
#define OE_OPTION_0_ARGS()
#define OE_OPTION_1_ARGS()
#define OE_OPTION_2_ARGS(TYPE, NAME) \
private: \
osgEarth::optional< TYPE > _ ## NAME ; \
mutable std::vector<std::function<void(const TYPE&)>> _ ## NAME ## _listeners; \
public: \
osgEarth::optional< TYPE >& NAME () { return _ ## NAME; } \
const osgEarth::optional< TYPE >& NAME () const { return _ ## NAME; } \
void NAME ## Changed (std::function<void(const TYPE&)> f) const { \
_ ## NAME ## _listeners.push_back(f); } \
void set_ ## NAME (const TYPE& value) { \
_ ## NAME = value; \
for(auto& notify : _ ## NAME ## _listeners) notify(value); }
#define OE_OPTION_3_ARGS(TYPE, NAME, DEFAULT_VALUE) \
private: \
osgEarth::optional< TYPE > _ ## NAME {DEFAULT_VALUE}; \
mutable std::vector<std::function<void(const TYPE&)>> _ ## NAME ## _listeners; \
public: \
osgEarth::optional< TYPE >& NAME () { return _ ## NAME; } \
const osgEarth::optional< TYPE >& NAME () const { return _ ## NAME; } \
void NAME ## Changed (std::function<void(const TYPE&)> f) const { \
_ ## NAME ## _listeners.push_back(f); } \
void set_ ## NAME (const TYPE& value) { \
_ ## NAME = value; \
for(auto& notify : _ ## NAME ## _listeners) notify(value); }
// https://stackoverflow.com/a/28074198
#define OE_OPTION_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
#define OE_OPTION_FUNC_RECOMPOSER(ARGS) OE_OPTION_FUNC_CHOOSER ARGS
#define OE_OPTION_CHOOSE_FROM_ARG_COUNT(...) OE_OPTION_FUNC_RECOMPOSER((__VA_ARGS__, OE_OPTION_3_ARGS, OE_OPTION_2_ARGS, OE_OPTION_1_ARGS, ))
#define OE_OPTION_NO_ARG_EXPANDER() ,,OE_OPTION_0_ARGS
#define OE_OPTION_MACRO_CHOOSER(...) OE_OPTION_CHOOSE_FROM_ARG_COUNT(OE_OPTION_NO_ARG_EXPANDER __VA_ARGS__ ())
#define OE_OPTION(...) OE_OPTION_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
//! ref_ptr property macro
#define OE_OPTION_REFPTR(TYPE, NAME) \
private: \
osg::ref_ptr< TYPE > _ ## NAME ; \
public: \
osg::ref_ptr< TYPE >& NAME () { return _ ## NAME; } \
const osg::ref_ptr< TYPE >& NAME () const { return _ ## NAME; }
//! ref_ptr property macro
#define OE_OPTION_SHAREDPTR(TYPE, NAME) \
private: \
std::shared_ptr< TYPE > _ ## NAME ; \
public: \
std::shared_ptr< TYPE >& NAME () { return _ ## NAME; } \
const std::shared_ptr< TYPE >& NAME () const { return _ ## NAME; }
//! vector property macro
#define OE_OPTION_VECTOR(TYPE, NAME) \
private: \
std::vector< TYPE > _ ## NAME ; \
public: \
std::vector< TYPE >& NAME () { return _ ## NAME; } \
const std::vector< TYPE >& NAME () const { return _ ## NAME; }
#define OE_OPTION_IMPL(CLASS, TYPE, FUNC, OPTION) \
void CLASS ::set ## FUNC (const TYPE & value) { options(). set_ ## OPTION (value); }\
const TYPE & CLASS ::get ## FUNC () const { return options(). OPTION ().get(); }
//! property macro
#define OE_PROPERTY(TYPE, NAME, DEFVAL) \
private: \
TYPE _ ## NAME = DEFVAL; \
public: \
TYPE & NAME () { return _ ## NAME; } \
TYPE & NAME ## _mutable() { return _ ## NAME; } \
const TYPE & NAME () const { return _ ## NAME; }
//! const property macro
#define OE_PROPERTY_CONST(TYPE, NAME, DEFVAL) \
private: \
TYPE _ ## NAME = DEFVAL; \
protected: \
TYPE & NAME () { return _ ## NAME; } \
TYPE & NAME ## _mutable() { return _ ## NAME; } \
public: \
const TYPE & NAME () const { return _ ## NAME; }
#define OE_OPTION_LESS(L,R) \
if (L().isSet() && !R().isSet()) return true; \
if (R().isSet() && !L().isSet()) return false; \
if (L().isSet() && R().isSet()) { \
if (L().get() < R().get()) return true; \
if (L().get() > R().get()) return false; }