diff options
author | Shaun Verch <shaun.verch@10gen.com> | 2013-09-18 14:02:36 -0400 |
---|---|---|
committer | Shaun Verch <shaun.verch@10gen.com> | 2013-10-04 16:58:54 -0400 |
commit | 6fc951d492881a32754bc3e38e8b5eca78929197 (patch) | |
tree | 73c2afe3120a8a87cf3bc8379e53461b2cf05b1e /src/mongo/util/options_parser | |
parent | 99372153dfe9dd9251e49371d872b5f27dfd6f2c (diff) | |
download | mongo-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.cpp | 144 | ||||
-rw-r--r-- | src/mongo/util/options_parser/environment.h | 36 | ||||
-rw-r--r-- | src/mongo/util/options_parser/environment_test.cpp | 55 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser_test.cpp | 21 |
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, §ionObjBuilder, 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) { |