summaryrefslogtreecommitdiff
path: root/src/mongo/util/options_parser
diff options
context:
space:
mode:
authorShaun Verch <shaun.verch@10gen.com>2013-09-18 14:02:36 -0400
committerShaun Verch <shaun.verch@10gen.com>2013-10-04 16:58:54 -0400
commit6fc951d492881a32754bc3e38e8b5eca78929197 (patch)
tree73c2afe3120a8a87cf3bc8379e53461b2cf05b1e /src/mongo/util/options_parser
parent99372153dfe9dd9251e49371d872b5f27dfd6f2c (diff)
downloadmongo-6fc951d492881a32754bc3e38e8b5eca78929197.tar.gz
SERVER-8510 Get rid of CmdLine struct and use new option handling style
Diffstat (limited to 'src/mongo/util/options_parser')
-rw-r--r--src/mongo/util/options_parser/environment.cpp144
-rw-r--r--src/mongo/util/options_parser/environment.h36
-rw-r--r--src/mongo/util/options_parser/environment_test.cpp55
-rw-r--r--src/mongo/util/options_parser/options_parser_test.cpp21
4 files changed, 212 insertions, 44 deletions
diff --git a/src/mongo/util/options_parser/environment.cpp b/src/mongo/util/options_parser/environment.cpp
index 41e94d90d40..cc4ad2a71aa 100644
--- a/src/mongo/util/options_parser/environment.cpp
+++ b/src/mongo/util/options_parser/environment.cpp
@@ -19,6 +19,8 @@
#include <iostream>
#include "mongo/bson/util/builder.h"
+#include "mongo/bson/bsonobjiterator.h"
+#include "mongo/db/jsobj.h"
#include "mongo/util/options_parser/constraints.h"
namespace mongo {
@@ -191,5 +193,147 @@ namespace optionenvironment {
}
}
+namespace {
+
+ // Converts a map of values with dotted key names to a BSONObj with sub objects.
+ // 1. Check for dotted field names and call valueMapToBSON recursively.
+ // 2. Append the actual value to our builder if we did not find a dot in our key name.
+ Status valueMapToBSON(const std::map<Key, Value>& params,
+ BSONObjBuilder* builder,
+ const std::string& prefix = std::string()) {
+ for (std::map<Key, Value>::const_iterator it(params.begin());
+ it != params.end(); it++) {
+ Key key = it->first;
+ Value value = it->second;
+
+ // 1. Check for dotted field names and call valueMapToBSON recursively.
+ // NOTE: this code depends on the fact that std::map is sorted
+ //
+ // EXAMPLE:
+ // The map:
+ // {
+ // "var1.dotted1" : false,
+ // "var2" : true,
+ // "var1.dotted2" : 6
+ // }
+ //
+ // Gets sorted by keys as:
+ // {
+ // "var1.dotted1" : false,
+ // "var1.dotted2" : 6,
+ // "var2" : true
+ // }
+ //
+ // Which means when we see the "var1" prefix, we can iterate until we see either a name
+ // without a dot or without "var1" as a prefix, aggregating its fields in a new map as
+ // we go. Because the map is sorted, once we see a name without a dot or a "var1"
+ // prefix we know that we've seen everything with "var1" as a prefix and can recursively
+ // build the entire sub object at once using our new map (which is the only way to make
+ // a single coherent BSON sub object using this append only builder).
+ //
+ // The result of this function for this example should be a BSON object of the form:
+ // {
+ // "var1" : {
+ // "dotted1" : false,
+ // "dotted2" : 6
+ // },
+ // "var2" : true
+ // }
+
+ // Check to see if this key name is dotted
+ std::string::size_type dotOffset = key.find('.');
+ if (dotOffset != string::npos) {
+
+ // Get the name of the "section" that we are currently iterating. This will be
+ // the name of our sub object.
+ std::string sectionName = key.substr(0, dotOffset);
+
+ // Build a map of the "section" that we are iterating to be passed in a
+ // recursive call.
+ std::map<Key, Value> sectionMap;
+
+ std::string beforeDot = key.substr(0, dotOffset);
+ std::string afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1);
+ std::map<Key, Value>::const_iterator it_next = it;
+
+ do {
+ // Here we know that the key at it_next has a dot and has the prefix we are
+ // currently creating a sub object for. Since that means we will definitely
+ // process that element in this loop, advance the outer for loop iterator here.
+ it = it_next;
+
+ // Add the value to our section map with a key of whatever is after the dot
+ // since the section name itself will be part of our sub object builder.
+ sectionMap[afterDot] = value;
+
+ // Peek at the next value for our iterator and check to see if we've finished.
+ if (++it_next == params.end()) {
+ break;
+ }
+ key = it_next->first;
+ value = it_next->second;
+
+ // Look for a dot for our next iteration.
+ dotOffset = key.find('.');
+
+ beforeDot = key.substr(0, dotOffset);
+ afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1);
+ }
+ while (dotOffset != string::npos && beforeDot == sectionName);
+
+ // Use the section name in our object builder, and recursively call
+ // valueMapToBSON with our sub map with keys that have the section name removed.
+ BSONObjBuilder sectionObjBuilder(builder->subobjStart(sectionName));
+ valueMapToBSON(sectionMap, &sectionObjBuilder, sectionName);
+ sectionObjBuilder.done();
+
+ // Our iterator is currently on the last field that matched our dot and prefix, so
+ // continue to the next loop iteration.
+ continue;
+ }
+
+ // 2. Append the actual value to our builder if we did not find a dot in our key name.
+ const type_info& type = value.type();
+
+ if (type == typeid(string)){
+ if (value.as<string>().empty()) {
+ // boost po uses empty string for flags like --quiet
+ // TODO: Remove this when we remove boost::program_options
+ builder->appendBool(key, true);
+ }
+ else {
+ builder->append(key, value.as<string>());
+ }
+ }
+ else if (type == typeid(int))
+ builder->append(key, value.as<int>());
+ else if (type == typeid(double))
+ builder->append(key, value.as<double>());
+ else if (type == typeid(bool))
+ builder->appendBool(key, value.as<bool>());
+ else if (type == typeid(long))
+ builder->appendNumber(key, (long long)value.as<long>());
+ else if (type == typeid(unsigned))
+ builder->appendNumber(key, (long long)value.as<unsigned>());
+ else if (type == typeid(unsigned long long))
+ builder->appendNumber(key, (long long)value.as<unsigned long long>());
+ else if (type == typeid(vector<string>))
+ builder->append(key, value.as<vector<string> >());
+ else
+ builder->append(key, "UNKNOWN TYPE: " + demangleName(type));
+ }
+ return Status::OK();
+ }
+} // namespace
+
+ BSONObj Environment::toBSON() const {
+ BSONObjBuilder builder;
+ Status ret = valueMapToBSON(values, &builder);
+ if (!ret.isOK()) {
+ return BSONObj();
+ }
+ return builder.obj();
+ }
+
} // namespace optionenvironment
} // namespace mongo
diff --git a/src/mongo/util/options_parser/environment.h b/src/mongo/util/options_parser/environment.h
index e2163ca5ecf..f7c8929814d 100644
--- a/src/mongo/util/options_parser/environment.h
+++ b/src/mongo/util/options_parser/environment.h
@@ -20,6 +20,7 @@
#include <vector>
#include "mongo/base/status.h"
+#include "mongo/db/jsobj.h"
#include "mongo/util/options_parser/value.h"
namespace mongo {
@@ -161,13 +162,36 @@ namespace optionenvironment {
Value operator[](const Key& key) const;
/**
- * Get all values that we have set explicitly as a map in case we need to iterate or
- * move to another structure, as is currently the use case for the parsed command line
- * options structure that we present to the user.
+ * Gets the BSON representation of this Environment. This will collapse dotted fields
+ * into sub objects.
+ *
+ * Example:
+ *
+ * The following Environment values map:
+ * "a.b.c" -> true
+ * "a.b.d" -> false
+ * "a.e.f" -> 0
+ * "a.e.g" -> 1
+ * "a.h" -> "foo"
+ *
+ * Has a BSON represation of (shown as JSON):
+ * { "a" : {
+ * "b" : {
+ * "c" : true,
+ * "d" : false
+ * },
+ * "e" : {
+ * "f" : 0,
+ * "g" : 1
+ * },
+ * "h" : "foo"
+ * }
+ * }
+ *
+ * Note that the BSON representation only includes fields that were explicitly set using
+ * setAll or set, and not defaults that were specified using setDefault.
*/
- const std::map<Key, Value>& getExplicitlySet() const {
- return values;
- }
+ BSONObj toBSON() const;
/* Debugging */
void dump();
diff --git a/src/mongo/util/options_parser/environment_test.cpp b/src/mongo/util/options_parser/environment_test.cpp
index 8893ea9db9f..959d1d08f7b 100644
--- a/src/mongo/util/options_parser/environment_test.cpp
+++ b/src/mongo/util/options_parser/environment_test.cpp
@@ -78,29 +78,44 @@ namespace {
ASSERT_EQUALS(number, 5);
}
- TEST(Environment, DefaultValueIterateExplicit) {
+ TEST(ToBSONTests, NormalValues) {
moe::Environment environment;
- ASSERT_OK(environment.setDefault(moe::Key("val1"), moe::Value(5)));
- ASSERT_OK(environment.setDefault(moe::Key("val2"), moe::Value(5)));
ASSERT_OK(environment.set(moe::Key("val1"), moe::Value(6)));
- int val1;
- ASSERT_OK(environment.get(moe::Key("val1"), &val1));
- ASSERT_EQUALS(val1, 6);
- int val2;
- ASSERT_OK(environment.get(moe::Key("val2"), &val2));
- ASSERT_EQUALS(val2, 5);
-
- const std::map<moe::Key, moe::Value> values = environment.getExplicitlySet();
- ASSERT_EQUALS((static_cast<std::map<moe::Key, moe::Value>::size_type>(1)), values.size());
+ ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(std::string("string"))));
+ mongo::BSONObj obj = BSON( "val1" << 6 << "val2" << "string" );
+ // TODO: Put a comparison here that doesn't depend on the field order. Right now it is
+ // based on the sort order of keys in a std::map.
+ ASSERT_EQUALS(obj, environment.toBSON());
+ }
- typedef std::map<moe::Key, moe::Value>::const_iterator it_type;
- for(it_type iterator = values.begin();
- iterator != values.end(); iterator++) {
- ASSERT_EQUALS(moe::Key("val1"), iterator->first);
- int val1;
- ASSERT_OK(iterator->second.get(&val1));
- ASSERT_EQUALS(6, val1);
- }
+ TEST(ToBSONTests, DottedValues) {
+ moe::Environment environment;
+ ASSERT_OK(environment.set(moe::Key("val1.dotted1"), moe::Value(6)));
+ ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(true)));
+ ASSERT_OK(environment.set(moe::Key("val1.dotted2"), moe::Value(std::string("string"))));
+ mongo::BSONObj obj = BSON("val1" << BSON( "dotted1" << 6 << "dotted2" << "string") <<
+ "val2" << true );
+ // TODO: Put a comparison here that doesn't depend on the field order. Right now it is
+ // based on the sort order of keys in a std::map.
+ ASSERT_EQUALS(obj, environment.toBSON());
}
+ TEST(ToBSONTests, DeepDottedValues) {
+ moe::Environment environment;
+ ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third1"), moe::Value(6)));
+ ASSERT_OK(environment.set(moe::Key("val1.first1.second2.third1"), moe::Value(false)));
+ ASSERT_OK(environment.set(moe::Key("val1.first2"), moe::Value(std::string("string"))));
+ ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third2"), moe::Value(true)));
+ ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(6.0)));
+ mongo::BSONObj obj = BSON("val1" << BSON("first1" <<
+ BSON("second1" <<
+ BSON("third1" << 6 << "third2" << true) <<
+ "second2" <<
+ BSON("third1" << false)) <<
+ "first2" << "string") <<
+ "val2" << 6.0);
+ // TODO: Put a comparison here that doesn't depend on the field order. Right now it is
+ // based on the sort order of keys in a std::map.
+ ASSERT_EQUALS(obj, environment.toBSON());
+ }
} // unnamed namespace
diff --git a/src/mongo/util/options_parser/options_parser_test.cpp b/src/mongo/util/options_parser/options_parser_test.cpp
index 9a97892fce8..33de98fb731 100644
--- a/src/mongo/util/options_parser/options_parser_test.cpp
+++ b/src/mongo/util/options_parser/options_parser_test.cpp
@@ -461,7 +461,7 @@ namespace {
ASSERT_EQUALS(port, 6);
}
- TEST(Parsing, DefaultValueIterateExplicit) {
+ TEST(Parsing, DefaultValuesNotInBSON) {
moe::OptionsParser parser;
moe::Environment environment;
@@ -480,23 +480,8 @@ namespace {
ASSERT_OK(parser.run(testOpts, argv, env_map, &environment));
- const std::map<moe::Key, moe::Value> values = environment.getExplicitlySet();
- ASSERT_EQUALS((static_cast<std::map<moe::Key, moe::Value>::size_type>(1)), values.size());
-
- typedef std::map<moe::Key, moe::Value>::const_iterator it_type;
- for(it_type iterator = values.begin();
- iterator != values.end(); iterator++) {
- ASSERT_EQUALS(moe::Key("val1"), iterator->first);
- int val1;
- ASSERT_OK(iterator->second.get(&val1));
- ASSERT_EQUALS(6, val1);
- }
-
- moe::Value value;
- ASSERT_OK(environment.get(moe::Key("val2"), &value));
- int val2;
- ASSERT_OK(value.get(&val2));
- ASSERT_EQUALS(val2, 5);
+ mongo::BSONObj expected = BSON("val1" << 6);
+ ASSERT_EQUALS(expected, environment.toBSON());
}
TEST(Parsing, ImplicitValue) {