/* Copyright 2013 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#pragma once
#include
#include
#include
#include "mongo/bson/mutable/const_element.h"
#include "mongo/bson/mutable/element.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
namespace mutablebson {
/** For an overview of mutable BSON, please see the file document.h in this directory.
*
* This file defines, in analogy with , a collection of useful algorithms for
* use with mutable BSON classes. In particular, algorithms for searching, sorting,
* indexed access, and counting are included.
*/
/** 'findElement' searches rightward among the sibiling Elements of 'first', returning an
* Element representing the first item matching the predicate 'predicate'. If no Element
* matches, then the 'ok' method on the returned Element will return false.
*/
template
inline ElementType findElement(ElementType first, Predicate predicate) {
while (first.ok() && !predicate(first))
first = first.rightSibling();
return first;
}
/** A predicate for findElement that matches on the field name of Elements. */
struct FieldNameEquals {
// The lifetime of this object must be a subset of the lifetime of 'fieldName'.
explicit FieldNameEquals(StringData fieldName)
: fieldName(fieldName) {}
bool operator()(const ConstElement& element) const {
return (fieldName == element.getFieldName());
}
StringData fieldName;
};
/** An overload of findElement that delegates to the special implementation
* Element::findElementNamed to reduce traffic across the Element API.
*/
template
inline ElementType findElement(ElementType first, FieldNameEquals predicate) {
return first.ok() ? first.findElementNamed(predicate.fieldName) : first;
}
/** A convenience wrapper around findElement. */
template
inline ElementType findElementNamed(ElementType first, StringData fieldName) {
return findElement(first, FieldNameEquals(fieldName));
}
/** Finds the first child under 'parent' that matches the given predicate. If no such child
* Element is found, the returned Element's 'ok' method will return false.
*/
template
inline ElementType findFirstChild(ElementType parent, Predicate predicate) {
return findElement(parent.leftchild(), predicate);
}
/** An overload of findFirstChild that delegates to the special implementation
* Element::findFirstChildNamed to reduce traffic across the Element API.
*/
template
inline ElementType findFirstChild(ElementType parent, FieldNameEquals predicate) {
return parent.ok() ? parent.findFirstChildNamed(predicate.fieldName) : parent;
}
/** Finds the first child under 'parent' that matches the given field name. If no such child
* Element is found, the returned Element's 'ok' method will return false.
*/
template
inline ElementType findFirstChildNamed(ElementType parent, StringData fieldName) {
return findFirstChild(parent, FieldNameEquals(fieldName));
}
/** A less-than ordering for Elements that compares based on the Element field names. */
class FieldNameLessThan {
// TODO: This should possibly derive from std::binary_function.
public:
inline bool operator()(const ConstElement& left, const ConstElement& right) const {
return left.getFieldName() < right.getFieldName();
}
};
/** Sort any children of Element 'parent' by way of Comparator 'comp', which should provide
* an operator() that takes two const Element&'s and implements a strict weak ordering.
*/
template
void sortChildren(Element parent, Comparator comp) {
// TODO: The following works, but obviously is not ideal.
// First, build a vector of the children.
std::vector children;
Element current = parent.leftChild();
while (current.ok()) {
children.push_back(current);
current = current.rightSibling();
}
// Then, sort the child vector with our comparator.
std::sort(children.begin(), children.end(), comp);
// Finally, reorder the children of parent according to the ordering established in
// 'children'.
std::vector::iterator where = children.begin();
const std::vector::iterator end = children.end();
for( ; where != end; ++where ) {
// Detach from its current location.
where->remove();
// Make it the new rightmost element.
parent.pushBack(*where);
}
}
/** Remove any consecutive children that compare as identical according to 'comp'. The
* children must be sorted (see sortChildren, above), and the equality comparator here
* must be compatible with the comparator used for the sort.
*/
template
void deduplicateChildren(Element parent, EqualityComparator equal) {
Element current = parent.leftChild();
while (current.ok()) {
Element next = current.rightSibling();
if (next.ok() && equal(current, next)) {
next.remove();
} else {
current = next;
}
}
}
/** A less-than ordering for Elements that compares based on woCompare */
class woLess {
// TODO: This should possibly derive from std::binary_function.
public:
woLess(bool considerFieldName = true)
: _considerFieldName(considerFieldName) {
}
inline bool operator()(const ConstElement& left, const ConstElement& right) const {
return left.compareWithElement(right, _considerFieldName) < 0;
}
private:
const bool _considerFieldName;
};
/** A greater-than ordering for Elements that compares based on woCompare */
class woGreater {
// TODO: This should possibly derive from std::binary_function.
public:
woGreater(bool considerFieldName = true)
: _considerFieldName(considerFieldName) {
}
inline bool operator()(const ConstElement& left, const ConstElement& right) const {
return left.compareWithElement(right, _considerFieldName) > 0;
}
private:
const bool _considerFieldName;
};
/** An equality predicate for elements that compares based on woCompare */
class woEqual {
// TODO: This should possibly derive from std::binary_function.
public:
woEqual(bool considerFieldName = true)
: _considerFieldName(considerFieldName) {
}
inline bool operator()(const ConstElement& left, const ConstElement& right) const {
return left.compareWithElement(right, _considerFieldName) == 0;
}
private:
const bool _considerFieldName;
};
/** An equality predicate for elements that compares based on woCompare */
class woEqualTo {
// TODO: This should possibly derive from std::binary_function.
public:
woEqualTo(const ConstElement& value, bool considerFieldName = true)
: _value(value)
, _considerFieldName(considerFieldName) {
}
inline bool operator()(const ConstElement& elt) const {
return _value.compareWithElement(elt, _considerFieldName) == 0;
}
private:
const ConstElement& _value;
const bool _considerFieldName;
};
// NOTE: Originally, these truly were algorithms, in that they executed the loop over a
// generic ElementType. However, these operations were later made intrinsic to
// Element/Document for performance reasons. These functions hare here for backward
// compatibility, and just delegate to the appropriate Element or ConstElement method of
// the same name.
/** Return the element that is 'n' Elements to the left in the sibling chain of 'element'. */
template
ElementType getNthLeftSibling(ElementType element, std::size_t n) {
return element.leftSibling(n);
}
/** Return the element that is 'n' Elements to the right in the sibling chain of 'element'. */
template
ElementType getNthRightSibling(ElementType element, std::size_t n) {
return element.rightSibling(n);
}
/** Move 'n' Elements left or right in the sibling chain of 'element' */
template
ElementType getNthSibling(ElementType element, int n) {
return (n < 0) ?
getNthLeftSibling(element, -n) :
getNthRightSibling(element, n);
}
/** Get the child that is 'n' Elements to the right of 'element's left child. */
template
ElementType getNthChild(ElementType element, std::size_t n) {
return element.findNthChild(n);
}
/** Returns the number of valid siblings to the left of 'element'. */
template
std::size_t countSiblingsLeft(ElementType element) {
return element.countSiblingsLeft();
}
/** Returns the number of valid siblings to the right of 'element'. */
template
std::size_t countSiblingsRight(ElementType element) {
return element.countSiblingsRight();
}
/** Return the number of children of 'element'. */
template
std::size_t countChildren(ElementType element) {
return element.countChildren();
}
/** Return the full (path) name of this element separating each name with the delim string. */
template
std::string getFullName(ElementType element, char delim = '.') {
std::vector names;
ElementType curr = element;
while(curr.ok() && curr.parent().ok()) {
names.push_back(curr.getFieldName());
curr = curr.parent();
}
mongoutils::str::stream name;
bool first = true;
for(std::vector::reverse_iterator it = names.rbegin();
it != names.rend();
++it) {
if (!first)
name << delim;
name << *it;
first = false;
}
return name;
}
} // namespace mutablebson
} // namespace mongo