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 ArduinoJson: change log
======================= =======================
HEAD
----
* Added `JsonArray::remove(iterator)` (issue #479)
* Added `JsonObject::remove(iterator)`
v5.8.4 v5.8.4
------ ------

View File

@ -48,6 +48,20 @@ class List {
return nodeCount; 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() { iterator begin() {
return iterator(_firstNode); return iterator(_firstNode);
} }
@ -62,22 +76,8 @@ class List {
return const_iterator(NULL); return const_iterator(NULL);
} }
protected: void remove(iterator it) {
node_type *addNewNode() { node_type *nodeToRemove = it._node;
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) {
if (!nodeToRemove) return; if (!nodeToRemove) return;
if (nodeToRemove == _firstNode) { if (nodeToRemove == _firstNode) {
_firstNode = nodeToRemove->next; _firstNode = nodeToRemove->next;
@ -87,6 +87,7 @@ class List {
} }
} }
protected:
JsonBuffer *_buffer; JsonBuffer *_buffer;
node_type *_firstNode; node_type *_firstNode;
}; };

View File

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

View File

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

View File

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

View File

@ -247,14 +247,14 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value, typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
bool>::type bool>::type
containsKey(const TString& key) const { containsKey(const TString& key) const {
return findNode<const TString&>(key) != NULL; return findKey<const TString&>(key) != end();
} }
// //
// bool containsKey(TKey); // bool containsKey(TKey);
// TKey = const char*, const char[N], const FlashStringHelper* // TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString> template <typename TString>
bool containsKey(const TString* key) const { 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. // 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, typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
void>::type void>::type
remove(const TString& key) { remove(const TString& key) {
removeNode(findNode<const TString&>(key)); remove(findKey<const TString&>(key));
} }
// //
// void remove(TKey); // void remove(TKey);
// TKey = const char*, const char[N], const FlashStringHelper* // TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString> template <typename TString>
void remove(const TString* key) { 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. // Returns a reference an invalid JsonObject.
// This object is meant to replace a NULL pointer. // This object is meant to replace a NULL pointer.
@ -286,41 +289,44 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
private: private:
// Returns the list node that matches the specified key. // Returns the list node that matches the specified key.
template <typename TStringRef> template <typename TStringRef>
node_type* findNode(TStringRef key) const { iterator findKey(TStringRef key) {
for (node_type* node = _firstNode; node; node = node->next) { iterator it;
if (Internals::StringTraits<TStringRef>::equals(key, node->content.key)) for (it = begin(); it != end(); ++it) {
return node; 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> template <typename TStringRef, typename TValue>
typename Internals::JsonVariantAs<TValue>::type get_impl( typename Internals::JsonVariantAs<TValue>::type get_impl(
TStringRef key) const { TStringRef key) const {
node_type* node = findNode<TStringRef>(key); const_iterator it = findKey<TStringRef>(key);
return node ? node->content.value.as<TValue>() return it != end() ? it->value.as<TValue>()
: Internals::JsonVariantDefault<TValue>::get(); : Internals::JsonVariantDefault<TValue>::get();
} }
template <typename TStringRef, typename TValueRef> template <typename TStringRef, typename TValueRef>
bool set_impl(TStringRef key, TValueRef value) { bool set_impl(TStringRef key, TValueRef value) {
node_type* node = findNode<TStringRef>(key); iterator it = findKey<TStringRef>(key);
if (!node) { if (it == end()) {
node = addNewNode(); it = Internals::List<JsonPair>::add();
if (!node) return false; if (it == end()) return false;
bool key_ok = Internals::ValueSetter<TStringRef>::set( bool key_ok =
_buffer, node->content.key, key); Internals::ValueSetter<TStringRef>::set(_buffer, it->key, key);
if (!key_ok) return false; if (!key_ok) return false;
} }
return Internals::ValueSetter<TValueRef>::set(_buffer, node->content.value, return Internals::ValueSetter<TValueRef>::set(_buffer, it->value, value);
value);
} }
template <typename TStringRef, typename TValue> template <typename TStringRef, typename TValue>
bool is_impl(TStringRef key) const { bool is_impl(TStringRef key) const {
node_type* node = findNode<TStringRef>(key); const_iterator it = findKey<TStringRef>(key);
return node ? node->content.value.is<TValue>() : false; return it != end() ? it->value.is<TValue>() : false;
} }
template <typename TStringRef> 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) #define TEST_(name) TEST_F(JsonArray_Remove_Tests, name)
TEST_(RemoveFirstElement) { TEST_(RemoveFirstByIndex) {
_array.removeAt(0); _array.removeAt(0);
EXPECT_EQ(2, _array.size()); EXPECT_EQ(2, _array.size());
@ -30,7 +30,7 @@ TEST_(RemoveFirstElement) {
EXPECT_STREQ("three", _array[1]); EXPECT_STREQ("three", _array[1]);
} }
TEST_(RemoveMiddleElement) { TEST_(RemoveMiddleByIndex) {
_array.removeAt(1); _array.removeAt(1);
EXPECT_EQ(2, _array.size()); EXPECT_EQ(2, _array.size());
@ -38,10 +38,40 @@ TEST_(RemoveMiddleElement) {
EXPECT_STREQ("three", _array[1]); EXPECT_STREQ("three", _array[1]);
} }
TEST_(RemoveLastElement) { TEST_(RemoveLastByIndex) {
_array.removeAt(2); _array.removeAt(2);
EXPECT_EQ(2, _array.size()); EXPECT_EQ(2, _array.size());
EXPECT_STREQ("one", _array[0]); EXPECT_STREQ("one", _array[0]);
EXPECT_STREQ("two", _array[1]); 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()); 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);
}