Added JsonArray::remove(iterator) and JsonObject::remove(iterator) (issue #479)

This commit is contained in:
Benoit Blanchon
2017-04-12 21:00:13 +02:00
parent 5a16b2117b
commit 8c6f64c111
8 changed files with 130 additions and 63 deletions

View File

@ -1,6 +1,12 @@
ArduinoJson: change log
=======================
HEAD
----
* Added `JsonArray::remove(iterator)` (issue #479)
* Added `JsonObject::remove(iterator)`
v5.8.4
------

View File

@ -48,6 +48,20 @@ class List {
return nodeCount;
}
iterator add() {
node_type *newNode = new (_buffer) node_type();
if (_firstNode) {
node_type *lastNode = _firstNode;
while (lastNode->next) lastNode = lastNode->next;
lastNode->next = newNode;
} else {
_firstNode = newNode;
}
return iterator(newNode);
}
iterator begin() {
return iterator(_firstNode);
}
@ -62,22 +76,8 @@ class List {
return const_iterator(NULL);
}
protected:
node_type *addNewNode() {
node_type *newNode = new (_buffer) node_type();
if (_firstNode) {
node_type *lastNode = _firstNode;
while (lastNode->next) lastNode = lastNode->next;
lastNode->next = newNode;
} else {
_firstNode = newNode;
}
return newNode;
}
void removeNode(node_type *nodeToRemove) {
void remove(iterator it) {
node_type *nodeToRemove = it._node;
if (!nodeToRemove) return;
if (nodeToRemove == _firstNode) {
_firstNode = nodeToRemove->next;
@ -87,6 +87,7 @@ class List {
}
}
protected:
JsonBuffer *_buffer;
node_type *_firstNode;
};

View File

@ -38,6 +38,14 @@ class ListConstIterator {
return *this;
}
ListConstIterator<T> &operator+=(size_t distance) {
while (_node && distance) {
_node = _node->next;
--distance;
}
return *this;
}
private:
const ListNode<T> *_node;
};

View File

@ -13,9 +13,14 @@
namespace ArduinoJson {
namespace Internals {
template <typename T>
class List;
// A read-write forward iterator for List<T>
template <typename T>
class ListIterator {
friend class List<T>;
public:
explicit ListIterator(ListNode<T> *node = NULL) : _node(node) {}
@ -39,6 +44,14 @@ class ListIterator {
return *this;
}
ListIterator<T> &operator+=(size_t distance) {
while (_node && distance) {
_node = _node->next;
--distance;
}
return *this;
}
operator ListConstIterator<T>() const {
return ListConstIterator<T>(_node);
}

View File

@ -111,16 +111,15 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
// Gets the value at the specified index.
template <typename T>
typename Internals::JsonVariantAs<T>::type get(size_t index) const {
node_type *node = findNode(index);
return node ? node->content.as<T>()
: Internals::JsonVariantDefault<T>::get();
const_iterator it = begin() += index;
return it != end() ? it->as<T>() : Internals::JsonVariantDefault<T>::get();
}
// Check the type of the value at specified index.
template <typename T>
bool is(size_t index) const {
node_type *node = findNode(index);
return node ? node->content.is<T>() : false;
const_iterator it = begin() += index;
return it != end() ? it->is<T>() : false;
}
// Creates a JsonArray and adds a reference at the end of the array.
@ -133,8 +132,9 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
// Removes element at specified index.
void removeAt(size_t index) {
removeNode(findNode(index));
remove(begin() += index);
}
using Internals::List<JsonVariant>::remove;
// Returns a reference an invalid JsonArray.
// This object is meant to replace a NULL pointer.
@ -198,28 +198,18 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
}
private:
node_type *findNode(size_t index) const {
node_type *node = _firstNode;
while (node && index--) node = node->next;
return node;
}
template <typename TValueRef>
bool set_impl(size_t index, TValueRef value) {
node_type *node = findNode(index);
if (!node) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, node->content,
value);
iterator it = begin() += index;
if (it == end()) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, *it, value);
}
template <typename TValueRef>
bool add_impl(TValueRef value) {
node_type *node = addNewNode();
if (!node) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, node->content,
value);
iterator it = Internals::List<JsonVariant>::add();
if (it == end()) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, *it, value);
}
};

View File

@ -247,14 +247,14 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
bool>::type
containsKey(const TString& key) const {
return findNode<const TString&>(key) != NULL;
return findKey<const TString&>(key) != end();
}
//
// bool containsKey(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString>
bool containsKey(const TString* key) const {
return findNode<const TString*>(key) != NULL;
return findKey<const TString*>(key) != end();
}
// Removes the specified key and the associated value.
@ -265,15 +265,18 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
void>::type
remove(const TString& key) {
removeNode(findNode<const TString&>(key));
remove(findKey<const TString&>(key));
}
//
// void remove(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString>
void remove(const TString* key) {
removeNode(findNode<const TString*>(key));
remove(findKey<const TString*>(key));
}
//
// void remove(iterator)
using Internals::List<JsonPair>::remove;
// Returns a reference an invalid JsonObject.
// This object is meant to replace a NULL pointer.
@ -286,41 +289,44 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
private:
// Returns the list node that matches the specified key.
template <typename TStringRef>
node_type* findNode(TStringRef key) const {
for (node_type* node = _firstNode; node; node = node->next) {
if (Internals::StringTraits<TStringRef>::equals(key, node->content.key))
return node;
iterator findKey(TStringRef key) {
iterator it;
for (it = begin(); it != end(); ++it) {
if (Internals::StringTraits<TStringRef>::equals(key, it->key)) break;
}
return NULL;
return it;
}
template <typename TStringRef>
const_iterator findKey(TStringRef key) const {
return const_cast<JsonObject*>(this)->findKey<TStringRef>(key);
}
template <typename TStringRef, typename TValue>
typename Internals::JsonVariantAs<TValue>::type get_impl(
TStringRef key) const {
node_type* node = findNode<TStringRef>(key);
return node ? node->content.value.as<TValue>()
const_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.as<TValue>()
: Internals::JsonVariantDefault<TValue>::get();
}
template <typename TStringRef, typename TValueRef>
bool set_impl(TStringRef key, TValueRef value) {
node_type* node = findNode<TStringRef>(key);
if (!node) {
node = addNewNode();
if (!node) return false;
iterator it = findKey<TStringRef>(key);
if (it == end()) {
it = Internals::List<JsonPair>::add();
if (it == end()) return false;
bool key_ok = Internals::ValueSetter<TStringRef>::set(
_buffer, node->content.key, key);
bool key_ok =
Internals::ValueSetter<TStringRef>::set(_buffer, it->key, key);
if (!key_ok) return false;
}
return Internals::ValueSetter<TValueRef>::set(_buffer, node->content.value,
value);
return Internals::ValueSetter<TValueRef>::set(_buffer, it->value, value);
}
template <typename TStringRef, typename TValue>
bool is_impl(TStringRef key) const {
node_type* node = findNode<TStringRef>(key);
return node ? node->content.value.is<TValue>() : false;
const_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.is<TValue>() : false;
}
template <typename TStringRef>

View File

@ -22,7 +22,7 @@ class JsonArray_Remove_Tests : public ::testing::Test {
#define TEST_(name) TEST_F(JsonArray_Remove_Tests, name)
TEST_(RemoveFirstElement) {
TEST_(RemoveFirstByIndex) {
_array.removeAt(0);
EXPECT_EQ(2, _array.size());
@ -30,7 +30,7 @@ TEST_(RemoveFirstElement) {
EXPECT_STREQ("three", _array[1]);
}
TEST_(RemoveMiddleElement) {
TEST_(RemoveMiddleByIndex) {
_array.removeAt(1);
EXPECT_EQ(2, _array.size());
@ -38,10 +38,40 @@ TEST_(RemoveMiddleElement) {
EXPECT_STREQ("three", _array[1]);
}
TEST_(RemoveLastElement) {
TEST_(RemoveLastByIndex) {
_array.removeAt(2);
EXPECT_EQ(2, _array.size());
EXPECT_STREQ("one", _array[0]);
EXPECT_STREQ("two", _array[1]);
}
TEST_(RemoveFirstByIterator) {
JsonArray::iterator it = _array.begin();
_array.remove(it);
EXPECT_EQ(2, _array.size());
EXPECT_STREQ("two", _array[0]);
EXPECT_STREQ("three", _array[1]);
}
TEST_(RemoveMiddleByIterator) {
JsonArray::iterator it = _array.begin();
++it;
_array.remove(it);
EXPECT_EQ(2, _array.size());
EXPECT_STREQ("one", _array[0]);
EXPECT_STREQ("three", _array[1]);
}
TEST_(RemoveLastByIterator) {
JsonArray::iterator it = _array.begin();
++it;
++it;
_array.remove(it);
EXPECT_EQ(2, _array.size());
EXPECT_STREQ("one", _array[0]);
EXPECT_STREQ("two", _array[1]);
}

View File

@ -29,3 +29,16 @@ TEST_(SizeUntouched_WhenRemoveIsCalledWithAWrongKey) {
EXPECT_EQ(1, _object.size());
}
TEST_(RemoveByIterator) {
DynamicJsonBuffer _jsonBuffer;
JsonObject& _object = _jsonBuffer.parseObject("{\"a\":0,\"b\":1,\"c\":2}");
for (JsonObject::iterator it = _object.begin(); it != _object.end(); ++it) {
if (it->value == 1) _object.remove(it);
}
char result[64];
_object.printTo(result);
EXPECT_STREQ("{\"a\":0,\"c\":2}", result);
}