/**
* 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 .
*/
#include "mongo/db/field_ref.h"
#include // for min
#include "mongo/util/log.h"
#include "mongo/util/assert_util.h"
namespace mongo {
void FieldRef::parse(const StringData& dottedField) {
if (dottedField.size() == 0) {
return;
}
if (_size != 0) {
clear();
}
// We guarantee that accesses through getPart() will be valid while 'this' is. So we
// take a copy. We're going to be "chopping" up the copy into c-strings.
_fieldBase.reset(new char[dottedField.size()+1]);
dottedField.copyTo( _fieldBase.get(), true );
// Separate the field parts using '.' as a delimiter.
char* beg = _fieldBase.get();
char* cur = beg;
char* end = beg + dottedField.size();
while (true) {
if (cur != end && *cur != '.') {
cur++;
continue;
}
appendPart(StringData(beg, cur - beg));
if (cur != end) {
*cur = '\0';
beg = ++cur;
continue;
}
break;
}
}
void FieldRef::setPart(size_t i, const StringData& part) {
dassert(i < _size);
if (_replacements.size() != _size) {
_replacements.resize(_size);
}
_replacements[i] = part.toString();
if (i < kReserveAhead) {
_fixed[i] = _replacements[i];
}
else {
_variable[getIndex(i)] = _replacements[i];
}
}
size_t FieldRef::appendPart(const StringData& part) {
if (_size < kReserveAhead) {
_fixed[_size] = part;
}
else {
_variable.push_back(part);
}
return ++_size;
}
StringData FieldRef::getPart(size_t i) const {
dassert(i < _size);
if (i < kReserveAhead) {
return _fixed[i];
}
else {
return _variable[getIndex(i)];
}
}
bool FieldRef::isPrefixOf( const FieldRef& other ) const {
// Can't be a prefix if the size is equal to or larger.
if ( _size >= other._size ) {
return false;
}
// Empty FieldRef is not a prefix of anything.
if ( _size == 0 ) {
return false;
}
size_t common = commonPrefixSize( other );
return common == _size && other._size > common;
}
size_t FieldRef::commonPrefixSize( const FieldRef& other ) const {
if (_size == 0 || other._size == 0) {
return 0;
}
size_t maxPrefixSize = std::min( _size-1, other._size-1 );
size_t prefixSize = 0;
while ( prefixSize <= maxPrefixSize ) {
if ( getPart( prefixSize ) != other.getPart( prefixSize ) ) {
break;
}
prefixSize++;
}
return prefixSize;
}
std::string FieldRef::dottedField( size_t offset ) const {
std::string res;
if (_size == 0 || offset >= numParts() ) {
return res;
}
for (size_t i=offset; i<_size; i++) {
if ( i > offset )
res.append(1, '.');
StringData part = getPart(i);
res.append(part.rawData(), part.size());
}
return res;
}
bool FieldRef::equalsDottedField( const StringData& other ) const {
StringData rest = other;
for ( size_t i = 0; i < _size; i++ ) {
StringData part = getPart( i );
if ( !rest.startsWith( part ) )
return false;
if ( i == _size - 1 )
return rest.size() == part.size();
// make sure next thing is a dot
if ( rest.size() == part.size() )
return false;
if ( rest[part.size()] != '.' )
return false;
rest = rest.substr( part.size() + 1 );
}
return false;
}
int FieldRef::compare(const FieldRef& other) const {
const size_t toCompare = std::min(_size, other._size);
for (size_t i = 0; i < toCompare; i++) {
if (getPart(i) == other.getPart(i)) {
continue;
}
return getPart(i) < other.getPart(i) ? -1 : 1;
}
const size_t rest = _size - toCompare;
const size_t otherRest = other._size - toCompare;
if ((rest == 0) && (otherRest == 0)) {
return 0;
}
else if (rest < otherRest ) {
return -1;
}
else {
return 1;
}
}
void FieldRef::clear() {
_size = 0;
_variable.clear();
_fieldBase.reset();
_replacements.clear();
}
size_t FieldRef::numReplaced() const {
size_t res = 0;
for (size_t i = 0; i < _replacements.size(); i++) {
if (!_replacements[i].empty()) {
res++;
}
}
return res;
}
std::ostream& operator<<(std::ostream& stream, const FieldRef& field) {
return stream << field.dottedField();
}
} // namespace mongo