configuration: added possibility to define property name aliases
The goal is to allow multiple names for properties. This will be used to replace legacy names like "evolutionsource", but it could also be used to allow abbreviations on the command line. First the patch replaces the single ConfigProperty::m_name with a list of names, of which the first one is the current name of the property. The name that is to be used depends on the node which is to be manipulated: if it already has a property under an alias, that alias is read and also written, so existing configuration continue to use the old config name (avoids the problem of having to remove the old name and insert the new one at the same spot in a .ini file). Old configs continue to parse okay. Writing into node which has none of the names set, as in migrating into a fresh config, uses the new names. Most of the patch deals with the removal of a single, unique name, either by picking the name so that it matches a node, using the default name or simply hard-coding a property name ("sync"). The command line accepts all aliases, then uses the main name for overriding configuration options.
This commit is contained in:
parent
a61b8590f3
commit
a6c245580c
|
@ -428,9 +428,9 @@ public:
|
|||
// configure active sources with the desired sync mode,
|
||||
// disable the rest
|
||||
FilterConfigNode::ConfigFilter filter;
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] = "none";
|
||||
filter["sync"] = "none";
|
||||
client.setConfigFilter(false, "", filter);
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] =
|
||||
filter["sync"] =
|
||||
PrettyPrintSyncMode(options.m_syncMode);
|
||||
for(int i = 0; sources[i] >= 0; i++) {
|
||||
std::string &name = m_syncSource2Config[sources[i]];
|
||||
|
|
|
@ -2730,10 +2730,10 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
BOOST_FOREACH(const ConfigProperty *prop, syncRegistry) {
|
||||
bool isDefault = false;
|
||||
string value = prop->getProperty(*syncConfig->getProperties(), &isDefault);
|
||||
if(boost::iequals(prop->getName(), "syncURL") && !syncURL.empty() ) {
|
||||
localConfigs.insert(pair<string, string>(prop->getName(), syncURL));
|
||||
if(boost::iequals(prop->getMainName(), "syncURL") && !syncURL.empty() ) {
|
||||
localConfigs.insert(pair<string, string>(prop->getMainName(), syncURL));
|
||||
} else if(!isDefault) {
|
||||
localConfigs.insert(pair<string, string>(prop->getName(), value));
|
||||
localConfigs.insert(pair<string, string>(prop->getMainName(), value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2763,7 +2763,7 @@ void ReadOperations::getConfig(bool getTemplate,
|
|||
bool isDefault = false;
|
||||
string value = prop->getProperty(*sourceNodes.getProperties(), &isDefault);
|
||||
if(!isDefault) {
|
||||
localConfigs.insert(pair<string, string>(prop->getName(), value));
|
||||
localConfigs.insert(pair<string, string>(prop->getMainName(), value));
|
||||
}
|
||||
}
|
||||
config.insert(pair<string, map<string, string> >( "source/" + name, localConfigs));
|
||||
|
@ -3233,7 +3233,7 @@ void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
|
|||
FilterConfigNode::ConfigFilter filter;
|
||||
filter = m_sourceFilter;
|
||||
if (!mode.empty()) {
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] = mode;
|
||||
filter["sync"] = mode;
|
||||
}
|
||||
m_sync->setConfigFilter(false, "", filter);
|
||||
BOOST_FOREACH(const std::string &source,
|
||||
|
@ -3241,7 +3241,7 @@ void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
|
|||
filter = m_sourceFilters[source];
|
||||
SourceModes_t::const_iterator it = source_modes.find(source);
|
||||
if (it != source_modes.end()) {
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] = it->second;
|
||||
filter["sync"] = it->second;
|
||||
}
|
||||
m_sync->setConfigFilter(false, source, filter);
|
||||
}
|
||||
|
@ -3705,12 +3705,12 @@ void Session::restore(const string &dir, bool before, const std::vector<std::str
|
|||
if(!sources.empty()) {
|
||||
BOOST_FOREACH(const std::string &source, sources) {
|
||||
FilterConfigNode::ConfigFilter filter;
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] = "two-way";
|
||||
filter["sync"] = "two-way";
|
||||
m_sync->setConfigFilter(false, source, filter);
|
||||
}
|
||||
// disable other sources
|
||||
FilterConfigNode::ConfigFilter disabled;
|
||||
disabled[SyncSourceConfig::m_sourcePropSync.getName()] = "disabled";
|
||||
disabled["sync"] = "disabled";
|
||||
m_sync->setConfigFilter(false, "", disabled);
|
||||
}
|
||||
m_restoreBefore = before;
|
||||
|
|
|
@ -132,7 +132,7 @@ bool Cmdline::parse(vector<string> &parsed)
|
|||
string cmdopt(m_argv[opt - 1]);
|
||||
if (!parseProp(m_validSourceProps, m_sourceProps,
|
||||
m_argv[opt - 1], opt == m_argc ? NULL : m_argv[opt],
|
||||
SyncSourceConfig::m_sourcePropSync.getName().c_str())) {
|
||||
"sync")) {
|
||||
return false;
|
||||
}
|
||||
parsed.push_back(m_argv[opt]);
|
||||
|
@ -799,7 +799,7 @@ bool Cmdline::run() {
|
|||
} else if (selected) {
|
||||
// user absolutely wants it: enable even if off by default
|
||||
FilterConfigNode::ConfigFilter::const_iterator sync =
|
||||
m_sourceProps.find(SyncSourceConfig::m_sourcePropSync.getName());
|
||||
m_sourceProps.find("sync");
|
||||
syncMode = sync == m_sourceProps.end() ? "two-way" : sync->second;
|
||||
}
|
||||
if (!syncMode.empty() &&
|
||||
|
@ -1146,7 +1146,7 @@ bool Cmdline::run() {
|
|||
// invalid source name in m_sources, remember and
|
||||
// report this below
|
||||
unmatchedSources.insert(source);
|
||||
} else if (m_sourceProps.find(SyncSourceConfig::m_sourcePropSync.getName()) ==
|
||||
} else if (m_sourceProps.find("sync") ==
|
||||
m_sourceProps.end()) {
|
||||
// Sync mode is not set, must override the
|
||||
// "sync=disabled" set below with the original
|
||||
|
@ -1156,7 +1156,7 @@ bool Cmdline::run() {
|
|||
// source activates it.
|
||||
FilterConfigNode::ConfigFilter filter = m_sourceProps;
|
||||
string sync = source_config->getSync();
|
||||
filter[SyncSourceConfig::m_sourcePropSync.getName()] =
|
||||
filter["sync"] =
|
||||
sync == "disabled" ? "two-way" : sync;
|
||||
context->setConfigFilter(false, source, filter);
|
||||
} else {
|
||||
|
@ -1168,7 +1168,7 @@ bool Cmdline::run() {
|
|||
|
||||
// temporarily disable the rest
|
||||
FilterConfigNode::ConfigFilter disabled;
|
||||
disabled[SyncSourceConfig::m_sourcePropSync.getName()] = "disabled";
|
||||
disabled["sync"] = "disabled";
|
||||
context->setConfigFilter(false, "", disabled);
|
||||
}
|
||||
|
||||
|
@ -1365,7 +1365,7 @@ bool Cmdline::listProperties(const ConfigPropertyRegistry &validProps,
|
|||
}
|
||||
comment = newComment;
|
||||
}
|
||||
m_out << prop->getName() << ":" << endl;
|
||||
m_out << prop->getMainName() << ":" << endl;
|
||||
}
|
||||
}
|
||||
dumpComment(m_out, " ", comment);
|
||||
|
@ -1503,7 +1503,7 @@ void Cmdline::dumpProperties(const ConfigNode &configuredProps,
|
|||
if (isDefault) {
|
||||
m_out << "# ";
|
||||
}
|
||||
m_out << prop->getName() << " = " << prop->getProperty(configuredProps) << endl;
|
||||
m_out << prop->getMainName() << " = " << prop->getProperty(configuredProps) << endl;
|
||||
|
||||
list<string> *type = NULL;
|
||||
switch (prop->getSharing()) {
|
||||
|
@ -1518,7 +1518,7 @@ void Cmdline::dumpProperties(const ConfigNode &configuredProps,
|
|||
break;
|
||||
}
|
||||
if (type) {
|
||||
type->push_back(prop->getName());
|
||||
type->push_back(prop->getMainName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,14 @@ MultiplexConfigNode::getNode(const string &property,
|
|||
const ConfigProperty **found) const
|
||||
{
|
||||
BOOST_FOREACH(const ConfigProperty *prop, m_registry) {
|
||||
if (boost::iequals(prop->getName(), property)) {
|
||||
bool match = false;
|
||||
BOOST_FOREACH(const std::string &name, prop->getNames()) {
|
||||
if (name == property) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if (found) {
|
||||
*found = prop;
|
||||
}
|
||||
|
|
|
@ -77,6 +77,28 @@ std::string ConfigLevel2String(ConfigLevel level)
|
|||
}
|
||||
}
|
||||
|
||||
string ConfigProperty::getName(const ConfigNode &node) const
|
||||
{
|
||||
if (m_names.empty()) {
|
||||
// shouldn't happen
|
||||
return "???";
|
||||
}
|
||||
if (m_names.size() == 1) {
|
||||
// typical case for most properties
|
||||
return m_names.front();
|
||||
}
|
||||
// pick the name already used in the node
|
||||
BOOST_FOREACH(const std::string &name, m_names) {
|
||||
string value;
|
||||
if (node.getProperty(name, value)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// main name as fallback
|
||||
return m_names.front();
|
||||
}
|
||||
|
||||
void ConfigProperty::splitComment(const string &comment, list<string> &commentLines)
|
||||
{
|
||||
size_t start = 0;
|
||||
|
@ -1692,7 +1714,7 @@ void PasswordConfigProperty::checkPassword(ConfigUserInterface &ui,
|
|||
string descr = getDescr(serverName,globalConfigNode,sourceName,sourceConfigNode);
|
||||
if (password == "-") {
|
||||
ConfigPasswordKey key = getPasswordKey(descr,serverName,globalConfigNode,sourceName,sourceConfigNode);
|
||||
passwordSave = ui.askPassword(getName(),descr, key);
|
||||
passwordSave = ui.askPassword(getMainName(),descr, key);
|
||||
} else if(boost::starts_with(password, "${") &&
|
||||
boost::ends_with(password, "}")) {
|
||||
string envname = password.substr(2, password.size() - 3);
|
||||
|
@ -1711,9 +1733,9 @@ void PasswordConfigProperty::checkPassword(ConfigUserInterface &ui,
|
|||
* Previous impl use temp string to store them, this is not good for expansion in the backend */
|
||||
if(!passwordSave.empty()) {
|
||||
if(sourceConfigNode.get() == NULL) {
|
||||
globalConfigNode.addFilter(getName(), passwordSave);
|
||||
globalConfigNode.addFilter(getMainName(), passwordSave);
|
||||
} else {
|
||||
sourceConfigNode->addFilter(getName(), passwordSave);
|
||||
sourceConfigNode->addFilter(getMainName(), passwordSave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1741,7 +1763,7 @@ void PasswordConfigProperty::savePassword(ConfigUserInterface &ui,
|
|||
}
|
||||
string descr = getDescr(serverName,globalConfigNode,sourceName,sourceConfigNode);
|
||||
ConfigPasswordKey key = getPasswordKey(descr,serverName,globalConfigNode,sourceName,sourceConfigNode);
|
||||
if(ui.savePassword(getName(), password, key)) {
|
||||
if(ui.savePassword(getMainName(), password, key)) {
|
||||
string value = "-";
|
||||
if(sourceConfigNode.get() == NULL) {
|
||||
setProperty(globalConfigNode, value);
|
||||
|
@ -2121,9 +2143,9 @@ static void copyProperties(const ConfigNode &fromProps,
|
|||
(unshared ||
|
||||
prop->getSharing() != ConfigProperty::NO_SHARING ||
|
||||
(prop->getFlags() & ConfigProperty::SHARED_AND_UNSHARED))) {
|
||||
string name = prop->getName();
|
||||
bool isDefault;
|
||||
string value = prop->getProperty(fromProps, &isDefault);
|
||||
string name = prop->getName(toProps);
|
||||
toProps.setProperty(name, value, prop->getComment(),
|
||||
isDefault ? &value : NULL);
|
||||
}
|
||||
|
@ -2710,7 +2732,7 @@ bool SecondsConfigProperty::checkValue(const string &value, string &error) const
|
|||
|
||||
unsigned int SecondsConfigProperty::getPropertyValue(const ConfigNode &node, bool *isDefault) const
|
||||
{
|
||||
string name = getName();
|
||||
string name = getName(node);
|
||||
string value = node.readProperty(name);
|
||||
if (value.empty()) {
|
||||
if (isDefault) {
|
||||
|
|
|
@ -131,6 +131,26 @@ class ConstSyncSourceNodes;
|
|||
/** name of the per-source admin data property */
|
||||
extern const char *const SourceAdminDataName;
|
||||
|
||||
/** simplified creation of string lists: InitList("foo") + "bar" + ... */
|
||||
template<class T> class InitList : public list<T> {
|
||||
public:
|
||||
InitList() {}
|
||||
InitList(const T &initialValue) {
|
||||
push_back(initialValue);
|
||||
}
|
||||
InitList &operator + (const T &rhs) {
|
||||
push_back(rhs);
|
||||
return *this;
|
||||
}
|
||||
InitList &operator += (const T &rhs) {
|
||||
push_back(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
typedef InitList<string> Aliases;
|
||||
typedef InitList<Aliases> Values;
|
||||
|
||||
|
||||
/**
|
||||
* A property has a name and a comment. Derived classes might have
|
||||
* additional code to read and write the property from/to a
|
||||
|
@ -138,6 +158,12 @@ extern const char *const SourceAdminDataName;
|
|||
* on the fly, therefore the virtual get methods which return a
|
||||
* string value and not just a reference.
|
||||
*
|
||||
* In addition to the name, it may also have aliases. When reading
|
||||
* from a ConfigNode, all specified names are checked in the order in
|
||||
* which they are listed, and the first one found is used. When
|
||||
* writing, an existing key is overwritten, otherwise the main name is
|
||||
* created as a new key.
|
||||
*
|
||||
* A default value is returned if the ConfigNode doesn't have
|
||||
* a value set (= empty string). Invalid values in the configuration
|
||||
* trigger an exception. Setting invalid values does not because
|
||||
|
@ -173,23 +199,41 @@ extern const char *const SourceAdminDataName;
|
|||
*/
|
||||
class ConfigProperty {
|
||||
public:
|
||||
ConfigProperty(const string &name, const string &comment,
|
||||
ConfigProperty(const string &name, const string &comment,
|
||||
const string &def = string(""), const string &descr = string("")) :
|
||||
m_obligatory(false),
|
||||
m_hidden(false),
|
||||
m_sharing(NO_SHARING),
|
||||
m_flags(0),
|
||||
m_names(name),
|
||||
m_comment(boost::trim_right_copy(comment)),
|
||||
m_defValue(def),
|
||||
m_descr(descr)
|
||||
{}
|
||||
|
||||
ConfigProperty(const Aliases &names, const string &comment,
|
||||
const string &def = string(""), const string &descr = string("")) :
|
||||
m_obligatory(false),
|
||||
m_hidden(false),
|
||||
m_sharing(NO_SHARING),
|
||||
m_flags(0),
|
||||
m_name(name),
|
||||
m_names(names),
|
||||
m_comment(boost::trim_right_copy(comment)),
|
||||
m_defValue(def),
|
||||
m_descr(descr)
|
||||
{}
|
||||
virtual ~ConfigProperty() {}
|
||||
|
||||
virtual string getName() const { return m_name; }
|
||||
virtual string getComment() const { return m_comment; }
|
||||
virtual string getDefValue() const { return m_defValue; }
|
||||
virtual string getDescr() const { return m_descr; }
|
||||
|
||||
/** name to be used for a specific node: first name if not in node, otherwise existing key */
|
||||
string getName(const ConfigNode &node) const;
|
||||
|
||||
/** primary name */
|
||||
string getMainName() const { return m_names.front(); }
|
||||
|
||||
const Aliases &getNames() const { return m_names; }
|
||||
string getComment() const { return m_comment; }
|
||||
string getDefValue() const { return m_defValue; }
|
||||
string getDescr() const { return m_descr; }
|
||||
|
||||
/**
|
||||
* Check whether the given value is okay.
|
||||
|
@ -278,19 +322,21 @@ class ConfigProperty {
|
|||
int getFlags(void) const { return m_flags; }
|
||||
|
||||
/** set value unconditionally, even if it is not valid */
|
||||
void setProperty(ConfigNode &node, const string &value) const { node.setProperty(getName(), value, getComment()); }
|
||||
void setProperty(ConfigNode &node, const string &value) const { node.setProperty(getName(node), value, getComment()); }
|
||||
void setProperty(FilterConfigNode &node, const string &value, bool temporarily = false) const {
|
||||
string name = getName(node);
|
||||
if (temporarily) {
|
||||
node.addFilter(m_name, value);
|
||||
node.addFilter(name, value);
|
||||
} else {
|
||||
node.setProperty(m_name, value, getComment());
|
||||
node.setProperty(name, value, getComment());
|
||||
}
|
||||
}
|
||||
|
||||
/** set default value of a property, marked as default unless forced setting */
|
||||
void setDefaultProperty(ConfigNode &node, bool force) const {
|
||||
string name = getName(node);
|
||||
string defValue = getDefValue();
|
||||
node.setProperty(m_name, defValue, getComment(), force ? NULL : &defValue);
|
||||
node.setProperty(name, defValue, getComment(), force ? NULL : &defValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -302,7 +348,7 @@ class ConfigProperty {
|
|||
* the default was returned instead
|
||||
*/
|
||||
virtual string getProperty(const ConfigNode &node, bool *isDefault = NULL) const {
|
||||
string name = getName();
|
||||
string name = getName(node);
|
||||
string value = node.readProperty(name);
|
||||
if (!value.empty()) {
|
||||
string error;
|
||||
|
@ -323,7 +369,7 @@ class ConfigProperty {
|
|||
|
||||
// true if property is set to non-empty value
|
||||
bool isSet(const ConfigNode &node) const {
|
||||
string name = getName();
|
||||
string name = getName(node);
|
||||
string value = node.readProperty(name);
|
||||
return !value.empty();
|
||||
}
|
||||
|
@ -336,28 +382,10 @@ class ConfigProperty {
|
|||
bool m_hidden;
|
||||
Sharing m_sharing;
|
||||
int m_flags;
|
||||
const string m_name, m_comment, m_defValue, m_descr;
|
||||
const Aliases m_names;
|
||||
const string m_comment, m_defValue, m_descr;
|
||||
};
|
||||
|
||||
template<class T> class InitList : public list<T> {
|
||||
public:
|
||||
InitList() {}
|
||||
InitList(const T &initialValue) {
|
||||
push_back(initialValue);
|
||||
}
|
||||
InitList &operator + (const T &rhs) {
|
||||
push_back(rhs);
|
||||
return *this;
|
||||
}
|
||||
InitList &operator += (const T &rhs) {
|
||||
push_back(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
typedef InitList<string> Aliases;
|
||||
typedef InitList<Aliases> Values;
|
||||
|
||||
|
||||
/**
|
||||
* A string property which maps multiple different possible value
|
||||
* strings to one generic value, ignoring the case. Values not listed
|
||||
|
@ -478,21 +506,22 @@ template<class T> class TypedConfigProperty : public ConfigProperty {
|
|||
ostringstream out;
|
||||
|
||||
out << value;
|
||||
node.setProperty(getName(), out.str(), getComment());
|
||||
node.setProperty(getName(node), out.str(), getComment());
|
||||
}
|
||||
void setProperty(FilterConfigNode &node, const T &value, bool temporarily = false) const {
|
||||
ostringstream out;
|
||||
string name = getName(node);
|
||||
|
||||
out << value;
|
||||
if (temporarily) {
|
||||
node.addFilter(getName(), out.str());
|
||||
node.addFilter(name, out.str());
|
||||
} else {
|
||||
node.setProperty(getName(), out.str(), getComment());
|
||||
node.setProperty(name, out.str(), getComment());
|
||||
}
|
||||
}
|
||||
|
||||
T getPropertyValue(const ConfigNode &node, bool *isDefault = NULL) const {
|
||||
string name = getName();
|
||||
string name = getName(node);
|
||||
string value = node.readProperty(name);
|
||||
istringstream in(value);
|
||||
T res;
|
||||
|
@ -687,6 +716,9 @@ class PasswordConfigProperty : public ConfigProperty {
|
|||
PasswordConfigProperty(const string &name, const string &comment, const string &def = string(""),const string &descr = string("")) :
|
||||
ConfigProperty(name, comment, def, descr)
|
||||
{}
|
||||
PasswordConfigProperty(const Aliases &names, const string &comment, const string &def = string(""),const string &descr = string("")) :
|
||||
ConfigProperty(names, comment, def, descr)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Check the password and cache the result.
|
||||
|
@ -835,8 +867,10 @@ class ConfigPropertyRegistry : public list<const ConfigProperty *> {
|
|||
/** case-insensitive search for property */
|
||||
const ConfigProperty *find(const string &propName) const {
|
||||
BOOST_FOREACH(const ConfigProperty *prop, *this) {
|
||||
if (boost::iequals(prop->getName(), propName)) {
|
||||
return prop;
|
||||
BOOST_FOREACH(const string &name, prop->getNames()) {
|
||||
if (boost::iequals(name, propName)) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
|
Loading…
Reference in New Issue