summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/bson_template_evaluator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/bson_template_evaluator.cpp')
-rw-r--r--src/mongo/scripting/bson_template_evaluator.cpp157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/mongo/scripting/bson_template_evaluator.cpp b/src/mongo/scripting/bson_template_evaluator.cpp
new file mode 100644
index 00000000000..b38bed37051
--- /dev/null
+++ b/src/mongo/scripting/bson_template_evaluator.cpp
@@ -0,0 +1,157 @@
+/** @file mongo/scripting/templateevaluator.cpp */
+
+/*
+ * Copyright (C) 2012 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mongo/scripting/bson_template_evaluator.h"
+
+#include <cstddef>
+#include <cstdlib>
+
+#include "mongo/util/map_util.h"
+
+namespace mongo {
+
+ void BsonTemplateEvaluator::initializeEvaluator() {
+ addOperator("RAND_INT", &BsonTemplateEvaluator::evalRandInt);
+ addOperator("RAND_STRING", &BsonTemplateEvaluator::evalRandString);
+ addOperator("CONCAT", &BsonTemplateEvaluator::evalConcat);
+ }
+
+ BsonTemplateEvaluator::BsonTemplateEvaluator() {
+ initializeEvaluator();
+ }
+
+ void BsonTemplateEvaluator::addOperator(const std::string& name, const OperatorFn& op) {
+ _operatorFunctions[name] = op;
+ }
+
+ BsonTemplateEvaluator::OperatorFn BsonTemplateEvaluator::operatorEvaluator(
+ const std::string& op) const {
+ return mapFindWithDefault(_operatorFunctions, op, OperatorFn());
+ }
+
+ /* This is the top level method for using this library. It takes a BSON Object as input,
+ * evaluates the templates and saves the result in the builder object.
+ * The method returns appropriate Status on success/error condition.
+ */
+ BsonTemplateEvaluator::Status BsonTemplateEvaluator::evaluate(const BSONObj& in,
+ BSONObjBuilder& builder) {
+ BSONForEach(e, in) {
+ Status st = _evalElem(e, builder);
+ if (st != StatusSuccess)
+ return st;
+ }
+ return StatusSuccess;
+ }
+
+ BsonTemplateEvaluator::Status BsonTemplateEvaluator::_evalElem(BSONElement in,
+ BSONObjBuilder& out) {
+ if (in.type() != Object) {
+ out.append(in);
+ return StatusSuccess;
+ }
+ BSONObj subObj = in.embeddedObject();
+ const char* opOrNot = subObj.firstElementFieldName();
+ if (opOrNot[0] != '#') {
+ out.append(in);
+ return StatusSuccess;
+ }
+ const char* op = opOrNot+1;
+ OperatorFn fn = operatorEvaluator(op);
+ if (!fn)
+ return StatusBadOperator;
+ Status st = fn(this, in.fieldName(), subObj, out);
+ if (st != StatusSuccess)
+ return st;
+ return StatusSuccess;
+ }
+
+ BsonTemplateEvaluator::Status BsonTemplateEvaluator::evalRandInt(BsonTemplateEvaluator* btl,
+ const char* fieldName,
+ const BSONObj in,
+ BSONObjBuilder& out) {
+ // in = { #RAND_INT: [10, 20] }
+ BSONObj range = in.firstElement().embeddedObject();
+ if (!range[0].isNumber() || !range[1].isNumber())
+ return StatusOpEvaluationError;
+ const int min = range["0"].numberInt();
+ const int max = range["1"].numberInt();
+ if (max <= min)
+ return StatusOpEvaluationError;
+ int randomNum = min + (rand() % (max - min));
+ if (range.nFields() == 3) {
+ if (!range[2].isNumber())
+ return StatusOpEvaluationError;
+ randomNum *= range[2].numberInt();
+ }
+ out.append(fieldName, randomNum);
+ return StatusSuccess;
+ }
+
+ BsonTemplateEvaluator::Status BsonTemplateEvaluator::evalRandString(BsonTemplateEvaluator* btl,
+ const char* fieldName,
+ const BSONObj in,
+ BSONObjBuilder& out) {
+ // in = { #RAND_STRING: [10] }
+ BSONObj range = in.firstElement().embeddedObject();
+ if (range.nFields() != 1)
+ return StatusOpEvaluationError;
+ if (!range[0].isNumber())
+ return StatusOpEvaluationError;
+ const int length = range["0"].numberInt();
+ if (length <= 0)
+ return StatusOpEvaluationError;
+ static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ static const size_t alphaNumLength = sizeof(alphanum) - 1;
+ BOOST_STATIC_ASSERT(alphaNumLength == 64);
+ unsigned currentRand = 0;
+ std::string str;
+ for (int i = 0; i < length; ++i, currentRand >>= 6) {
+ if (i % 5 == 0)
+ currentRand = rand();
+ str.push_back(alphanum[currentRand % alphaNumLength]);
+ }
+ out.append(fieldName, str);
+ return StatusSuccess;
+ }
+
+ BsonTemplateEvaluator::Status BsonTemplateEvaluator::evalConcat(BsonTemplateEvaluator* btl,
+ const char* fieldName,
+ const BSONObj in,
+ BSONObjBuilder& out) {
+ // in = { #CONCAT: ["hello", " ", "world"] }
+ BSONObjBuilder objectBuilder;
+ Status status = btl->evaluate(in.firstElement().embeddedObject(), objectBuilder);
+ if (status != StatusSuccess)
+ return status;
+ BSONObj parts = objectBuilder.obj();
+ if (parts.nFields() <= 1)
+ return StatusOpEvaluationError;
+ StringBuilder stringBuilder;
+ BSONForEach(part, parts) {
+ if (part.type() == String)
+ stringBuilder << part.String();
+ else
+ part.toString(stringBuilder,false);
+ }
+ out.append(fieldName, stringBuilder.str());
+ return StatusSuccess;
+ }
+
+} // end namespace mongo