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:
Patrick Ohly 2011-01-18 15:18:21 +01:00
parent a61b8590f3
commit a6c245580c
6 changed files with 127 additions and 64 deletions

View File

@ -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]];

View File

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

View File

@ -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());
}
}

View File

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

View File

@ -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) {

View File

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