792 lines
27 KiB
Plaintext
792 lines
27 KiB
Plaintext
|
/* -*-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; }
|