/* 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 "mongo/base/status.h"
#include "mongo/base/string_data.h"
#include "mongo/db/jsobj.h"
#include "mongo/util/safe_num.h"
namespace mongo {
namespace mutablebson {
/** For an overview of mutable BSON, please see the file document.h in this directory. */
class ConstElement;
class Document;
/** Element represents a BSON value or object in a mutable BSON Document. The lifetime of
* an Element is a subset of the Document to which it belongs. Much like a BSONElement, an
* Element has a type, a field name, and (usually) a value. An Element may be used to read
* or modify the value (including changing its type), to navigate to related Elements in
* the Document tree, or for a number of topological changes to the Document
* structure. Element also offers the ability to compare its value to that of other
* Elements, and to serialize its value to a BSONObjBuilder or BSONArrayBuilder.
*
* Elements have reference or iterator like semantics, and are very lightweight. You
* should not worry about the cost of passing an Element by value, copying an Element, or
* similar operations. Such operations do not mean that the logical element in the
* underlying Document is duplicated. Only the reference is duplicated.
*
* The API for Element is broken into several sections:
*
* - Topology mutation: These methods are to either add other Elements to the Document
* tree as siblings or children (when applicable) of the current Element, to remove the
* Element from the tree, or to remove children of the Element (when applicable).
*
* - Navigation: These methods are used to navigate the Document tree by returning other
* Elements in specified relationships to the current Element. In this regard, Elements
* act much like STL iterators that walk over the Document tree. One important
* difference is that Elements are never invalidated, even when 'remove' is called. If
* you have two Elements that alias the same element in the Document tree, modifications
* through one Element will be visible via the other.
*
* - Value access: These methods provide access to the value in the Document tree that the
* current Element represents. All leaf (a.k.a. 'primitive', or non-Object and
* non-Array) like Elements will always be able to provide a value. However, there are
* cases where non-leaf Elements (representing Objects or Arrays) cannot provide a
* value. Therefore, you must always call 'hasValue' to determine if the value is
* available before calling 'getValue'. Similarly, you must determine the type of the
* Element by calling getType() and only call the matching typed getValue.
*
* - Comparison: It is possible to compare one Element with another to determine ordering
* or equality as defined by woCompare. Similarly, it is possible to directly compare an
* Element with a BSONElement. It is legal to compare two Elements which belong to
* different Documents.
*
* - Serialization: Elements may be serialized to BSONObjBuilder or to BSONArrayBuilder
* objects when appropriate. One detail to consider is that writeTo for the root Element
* behaves differently than the others: it does not start a new subobj scope in the
* builder, so all of its children will be added at the current level to the
* builder. The provided builder does not have its 'done' method called automatically.
*
* - Value mutation: You may freely modify the value of an Element, including
* modifications that change the type of the Element and setting the value of the
* Element to the value of another BSONObj. You may also set the value from a SafeNum or
* from a BSONElement.
*
* - Accessors: These provide access to various properties of the Element, like the
* Document to which the Element belongs, the BSON type and field name of the Element,
* etc. One critical accessor is 'ok'. When using the topology API to navigate a
* document, it is possible to request an Element which does not exist, like the parent
* of the root element, or the left child of an integer, or the right sibling of the
* last element in an array. In these cases, the topology API will return an Element for
* which the 'ok' accessor will return 'false', which is roughly analagous to an 'end'
* valued STL iterator. It is illegal to call any method (other than 'ok') on a non-OK
* Element.
*
* - Streaming API: As a convenience for when you are building Documents from scratch, an
* API is provided that combines the effects of calling makeElement on the Document with
* calling pushBack on the current Element. The effect is to create the element and make
* it the new rightmost child of this Element. Use of this API is discouraged and it may
* be removed.
*/
class Element {
public:
typedef uint32_t RepIdx;
// Some special RepIdx values. These are really implementation details, but they are
// here so that we can inline Element::OK, which gets called very frequently, and they
// need to be public so some free functions in document.cpp can use them. You must not
// use these values explicitly.
// Used to signal an invalid Element.
static const RepIdx kInvalidRepIdx = RepIdx(-1);
// A rep that points to an unexamined entity
static const RepIdx kOpaqueRepIdx = RepIdx(-2);
// This is the highest valid rep that does not overlap flag values.
static const RepIdx kMaxRepIdx = RepIdx(-3);
//
// Topology mutation API. Element arguments must belong to the same Document.
//
/** Add the provided Element to the left of this Element. The added Element must be
* 'ok', and this Element must have a parent.
*/
Status addSiblingLeft(Element e);
/** Add the provided Element to the right of this Element. The added Element must be
* 'ok', and this Element must have a parent.
*/
Status addSiblingRight(Element e);
/** 'Remove' this Element by detaching it from its parent and siblings. The Element
* continues to exist and may be manipulated, but cannot be re-obtained by navigating
* from the root.
*/
Status remove();
/** If this Element is empty, add 'e' as the first child. Otherwise, add 'e' as the new
* left child.
*/
Status pushFront(Element e);
/** If this Element is empty, add 'e' as the first child. Otherwise, add 'e' as the new
* right child.
*/
Status pushBack(Element e);
/** Remove the leftmost child Element if it exists, otherwise return an error. */
Status popFront();
/** Remove the rightmost child Element if it exists, otherwise return an error. */
Status popBack();
/** Rename this Element to the provided name. */
Status rename(StringData newName);
//
// Navigation API.
//
/** Returns either this Element's left child, or a non-ok Element if no left child
* exists.
*/
Element leftChild() const;
/** Returns either this Element's right child, or a non-ok Element if no right child
* exists. Note that obtaining the right child may require realizing all immediate
* child nodes of a document that is being consumed lazily.
*/
Element rightChild() const;
/** Returns true if this element has children. Always returns false if this Element is
* not an Object or Array.
*/
bool hasChildren() const;
/** Returns either this Element's sibling 'distance' elements to the left, or a non-ok
* Element if no such left sibling exists.
*/
Element leftSibling(size_t distance = 1) const;
/** Returns either this Element's sibling 'distance' Elements to the right, or a non-ok
* Element if no such right sibling exists.
*/
Element rightSibling(size_t distance = 1) const;
/** Returns this Element's parent, or a non-ok Element if this Element has no parent
* (is a root).
*/
Element parent() const;
/** Returns the nth child, if any, of this Element. If no such element exists, a non-ok
* Element is returned. This is not a constant time operation. This method is also
* available as operator[] taking a size_t for convenience.
*/
Element findNthChild(size_t n) const;
inline Element operator[](size_t n) const;
/** Returns the first child, if any, of this Element named 'name'. If no such Element
* exists, a non-ok Element is returned. This is not a constant time operation. It is illegal
* to call this on an Array. This method is also available as operator[] taking a StringData
* for convenience.
*/
Element findFirstChildNamed(StringData name) const;
inline Element operator[](StringData name) const;
/** Returns the first element found named 'name', starting the search at the current
* Element, and walking right. If no such Element exists, a non-ok Element is
* returned. This is not a constant time operation. This implementation is used in the
* specialized implementation of findElement.
*/
Element findElementNamed(StringData name) const;
//
// Counting API.
//
/** Returns the number of valid siblings to the left of this Element. */
size_t countSiblingsLeft() const;
/** Returns the number of valid siblings to the right of this Element. */
size_t countSiblingsRight() const;
/** Return the number of children of this Element. */
size_t countChildren() const;
//
// Value access API.
//
// We only provide accessors for BSONElement and for simple types. For more complex
// types like regex you should obtain the BSONElement and use that API to extract the
// components.
//
// Note that the getValueX methods are *unchecked* in release builds: You are
// responsible for calling hasValue() to ensure that this element has a value
// representation, and for calling getType to ensure that the Element is of the proper
// type.
//
// As usual, methods here are in bsonspec type order, please keep them that way.
//
/** Returns true if 'getValue' can return a valid BSONElement from which a value may be
* extracted. See the notes for 'getValue' to understand the conditions under which an
* Element can provide a BSONElement.
*/
bool hasValue() const;
/** Returns true if this element is a numeric type (e.g. NumberLong). Currently, the
* only numeric BSON types are NumberLong, NumberInt, NumberDouble, and NumberDecimal.
*/
bool isNumeric() const;
/** Returns true if this element is one of the integral numeric types (e.g. NumberLong
* or NumberInt).
*/
bool isIntegral() const;
/** Get the value of this element if available. Note that not all elements have a
* representation as a BSONElement. For elements that do have a representation, this
* will return it. For elements that do not this method returns an eoo
* BSONElement. All 'value-ish' Elements will have a BSONElement
* representation. 'Tree-ish' Elements may or may not have a BSONElement
* representation. Mutations may cause elements to change whether or not they have a
* value and may invalidate previously returned values.
*
* Please note that a const BSONElement allows retrieval of a non-const
* BSONObj. However, the contents of the BSONElement returned here must be treated as
* const.
*/
const BSONElement getValue() const;
/** Get the value from a double valued Element. */
inline double getValueDouble() const;
/** Get the value from a std::string valued Element. */
inline StringData getValueString() const;
/** Get the value from an object valued Element. Note that this may not always be
* possible!
*/
inline BSONObj getValueObject() const;
/** Get the value from an object valued Element. Note that this may not always be
* possible!
*/
inline BSONArray getValueArray() const;
/** Returns true if this Element is the undefined type. */
inline bool isValueUndefined() const;
/** Get the value from an OID valued Element. */
inline OID getValueOID() const;
/** Get the value from a bool valued Element. */
inline bool getValueBool() const;
/** Get the value from a date valued Element. */
inline Date_t getValueDate() const;
/** Returns true if this Element is the null type. */
inline bool isValueNull() const;
/** Get the value from a symbol valued Element. */
inline StringData getValueSymbol() const;
/** Get the value from an int valued Element. */
inline int32_t getValueInt() const;
/** Get the value from a timestamp valued Element. */
inline Timestamp getValueTimestamp() const;
/** Get the value from a long valued Element. */
inline int64_t getValueLong() const;
/** Get the value from a decimal valued Element. */
inline Decimal128 getValueDecimal() const;
/** Returns true if this Element is the min key type. */
inline bool isValueMinKey() const;
/** Returns true if this Element is the max key type. */
inline bool isValueMaxKey() const;
/** Returns the numeric value as a SafeNum */
SafeNum getValueSafeNum() const;
//
// Comparision API.
//
/** Compare this Element with Element 'other'. The two Elements may belong to different
* Documents. You should not call this on the root Element of the Document because the
* root Element does not have a field name. Use compareWithBSONObj to handle that
* case.
*
* Returns -1 if this < other according to BSONElement::woCompare
* Returns 0 if this == other either tautologically, or according to woCompare.
* Returns 1 if this > other according to BSONElement::woCompare
*/
int compareWithElement(const ConstElement& other,
const StringData::ComparatorInterface* comparator,
bool considerFieldName = true) const;
/** Compare this Element with BSONElement 'other'. You should not call this on the root
* Element of the Document because the root Element does not have a field name. Use
* compareWithBSONObj to handle that case.
*
* Returns -1 if this < other according to BSONElement::woCompare
* Returns 0 if this == other either tautologically, or according to woCompare.
* Returns 1 if this > other according to BSONElement::woCompare
*/
int compareWithBSONElement(const BSONElement& other,
const StringData::ComparatorInterface* comparator,
bool considerFieldName = true) const;
/** Compare this Element, which must be an Object or an Array, with 'other'.
*
* Returns -1 if this object < other according to BSONElement::woCompare
* Returns 0 if this object == other either tautologically, or according to woCompare.
* Returns 1 if this object > other according to BSONElement::woCompare
*/
int compareWithBSONObj(const BSONObj& other,
const StringData::ComparatorInterface* comparator,
bool considerFieldName = true) const;
//
// Serialization API.
//
/** Write this Element to the provided object builder. */
void writeTo(BSONObjBuilder* builder) const;
/** Write this Element to the provided array builder. This Element must be of type
* mongo::Array.
*/
void writeArrayTo(BSONArrayBuilder* builder) const;
//
// Value mutation API. Please note that the types are ordered according to bsonspec.org
// ordering. Please keep them that way.
//
/** Set the value of this Element to the given double. */
Status setValueDouble(double value);
/** Set the value of this Element to the given string. */
Status setValueString(StringData value);
/** Set the value of this Element to the given object. The data in 'value' is
* copied.
*/
Status setValueObject(const BSONObj& value);
/** Set the value of this Element to the given object. The data in 'value' is
* copied.
*/
Status setValueArray(const BSONObj& value);
/** Set the value of this Element to the given binary data. */
Status setValueBinary(uint32_t len, mongo::BinDataType binType, const void* data);
/** Set the value of this Element to Undefined. */
Status setValueUndefined();
/** Set the value of this Element to the given OID. */
Status setValueOID(OID value);
/** Set the value of this Element to the given boolean. */
Status setValueBool(bool value);
/** Set the value of this Element to the given date. */
Status setValueDate(Date_t value);
/** Set the value of this Element to Null. */
Status setValueNull();
/** Set the value of this Element to the given regex parameters. */
Status setValueRegex(StringData re, StringData flags);
/** Set the value of this Element to the given db ref parameters. */
Status setValueDBRef(StringData ns, OID oid);
/** Set the value of this Element to the given code data. */
Status setValueCode(StringData value);
/** Set the value of this Element to the given symbol. */
Status setValueSymbol(StringData value);
/** Set the value of this Element to the given code and scope data. */
Status setValueCodeWithScope(StringData code, const BSONObj& scope);
/** Set the value of this Element to the given integer. */
Status setValueInt(int32_t value);
/** Set the value of this Element to the given timestamp. */
Status setValueTimestamp(Timestamp value);
/** Set the value of this Element to the given long integer */
Status setValueLong(int64_t value);
/** Set the value of this Element to the given decimal. */
Status setValueDecimal(Decimal128 value);
/** Set the value of this Element to MinKey. */
Status setValueMinKey();
/** Set the value of this Element to MaxKey. */
Status setValueMaxKey();
//
// Value mutation API from variant types.
//
/** Set the value of this element to equal the value of the provided BSONElement
* 'value'. The name of this Element is not modified.
*
* The contents of value are copied.
*/
Status setValueBSONElement(const BSONElement& value);
/** Set the value of this Element to a numeric type appropriate to hold the given
* SafeNum value.
*/
Status setValueSafeNum(const SafeNum value);
/** Set the value of this Element to the value from another Element.
*
* The name of this Element is not modified.
*/
Status setValueElement(ConstElement setFrom);
//
// Accessors
//
/** Returns true if this Element represents a valid part of the Document. */
inline bool ok() const;
/** Returns the Document to which this Element belongs. */
inline Document& getDocument();
/** Returns the Document to which this Element belongs. */
inline const Document& getDocument() const;
/** Returns the BSONType of this Element. */
BSONType getType() const;
/** Returns true if this Element is of the specified type */
inline bool isType(BSONType type) const;
/** Returns the field name of this Element. Note that the value returned here is not
* stable across mutations, since the storage for fieldNames may be reallocated. If
* you need a stable version of the fieldName, you must call toString on the returned
* StringData.
*/
StringData getFieldName() const;
/** Returns the opaque ID for this element. This is unlikely to be useful to a caller
* and is mostly for testing.
*/
inline RepIdx getIdx() const;
//
// Stream API - BSONObjBuilder like API, but methods return a Status. These are
// strictly a convenience API. You don't need to use them if you would rather be more
// explicit.
//
/** Append the provided double value as a new field with the provided name. */
Status appendDouble(StringData fieldName, double value);
/** Append the provided std::string value as a new field with the provided name. */
Status appendString(StringData fieldName, StringData value);
/** Append the provided object as a new field with the provided name. The data in
* 'value' is copied.
*/
Status appendObject(StringData fieldName, const BSONObj& value);
/** Append the provided array object as a new field with the provided name. The data in
* value is copied.
*/
Status appendArray(StringData fieldName, const BSONObj& value);
/** Append the provided binary data as a new field with the provided name. */
Status appendBinary(StringData fieldName,
uint32_t len,
mongo::BinDataType binType,
const void* data);
/** Append an undefined value as a new field with the provided name. */
Status appendUndefined(StringData fieldName);
/** Append the provided OID as a new field with the provided name. */
Status appendOID(StringData fieldName, mongo::OID value);
/** Append the provided bool as a new field with the provided name. */
Status appendBool(StringData fieldName, bool value);
/** Append the provided date as a new field with the provided name. */
Status appendDate(StringData fieldName, Date_t value);
/** Append a null as a new field with the provided name. */
Status appendNull(StringData fieldName);
/** Append the provided regex data as a new field with the provided name. */
Status appendRegex(StringData fieldName, StringData re, StringData flags);
/** Append the provided DBRef data as a new field with the provided name. */
Status appendDBRef(StringData fieldName, StringData ns, mongo::OID oid);
/** Append the provided code data as a new field with the iven name. */
Status appendCode(StringData fieldName, StringData value);
/** Append the provided symbol data as a new field with the provided name. */
Status appendSymbol(StringData fieldName, StringData value);
/** Append the provided code and scope data as a new field with the provided name. */
Status appendCodeWithScope(StringData fieldName, StringData code, const BSONObj& scope);
/** Append the provided integer as a new field with the provided name. */
Status appendInt(StringData fieldName, int32_t value);
/** Append the provided timestamp as a new field with the provided name. */
Status appendTimestamp(StringData fieldName, Timestamp value);
/** Append the provided long integer as a new field with the provided name. */
Status appendLong(StringData fieldName, int64_t value);
/** Append the provided decimal as a new field with the provided name. */
Status appendDecimal(StringData fieldName, Decimal128 value);
/** Append a max key as a new field with the provided name. */
Status appendMinKey(StringData fieldName);
/** Append a min key as a new field with the provided name. */
Status appendMaxKey(StringData fieldName);
/** Append the given BSONElement. The data in 'value' is copied. */
Status appendElement(const BSONElement& value);
/** Append the provided number as field of the appropriate numeric type with the
* provided name.
*/
Status appendSafeNum(StringData fieldName, SafeNum value);
/** Convert this element to its JSON representation if ok(),
* otherwise return !ok() message */
std::string toString() const;
private:
friend class Document;
friend class ConstElement;
friend bool operator==(const Element&, const Element&);
inline Element(Document* doc, RepIdx repIdx);
Status addChild(Element e, bool front);
StringData getValueStringOrSymbol() const;
Status setValue(Element::RepIdx newValueIdx);
Document* _doc;
RepIdx _repIdx;
};
/** Element comparison support. Comparison is like STL iterator comparision: equal Elements
* refer to the same underlying data. The equality does *not* mean that the underlying
* values are equivalent. Use the Element::compareWith methods to compare the represented
* data.
*/
/** Returns true if l and r refer to the same data, false otherwise. */
inline bool operator==(const Element& l, const Element& r);
/** Returns false if l and r refer to the same data, true otherwise. */
inline bool operator!=(const Element& l, const Element& r);
} // namespace mutablebson
} // namespace mongo
#include "mongo/bson/mutable/element-inl.h"