From 186aefb8f2c0b422b17bedea3a615accc4ee801e Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sun, 12 Jul 2015 18:28:23 +0200
Subject: [PATCH] added first insert functions

---
 README.md                         |   2 +-
 doc/examples/insert.cpp           |  16 +++
 doc/examples/insert.link          |   1 +
 doc/examples/insert.output        |   2 +
 doc/examples/insert__count.cpp    |  16 +++
 doc/examples/insert__count.link   |   1 +
 doc/examples/insert__count.output |   2 +
 doc/examples/insert__range.cpp    |  19 ++++
 doc/examples/insert__range.link   |   1 +
 doc/examples/insert__range.output |   2 +
 src/json.hpp                      | 144 ++++++++++++++++++++++++++-
 src/json.hpp.re2c                 | 144 ++++++++++++++++++++++++++-
 test/unit.cpp                     | 156 ++++++++++++++++++++++++++++++
 13 files changed, 503 insertions(+), 3 deletions(-)
 create mode 100644 doc/examples/insert.cpp
 create mode 100644 doc/examples/insert.link
 create mode 100644 doc/examples/insert.output
 create mode 100644 doc/examples/insert__count.cpp
 create mode 100644 doc/examples/insert__count.link
 create mode 100644 doc/examples/insert__count.output
 create mode 100644 doc/examples/insert__range.cpp
 create mode 100644 doc/examples/insert__range.link
 create mode 100644 doc/examples/insert__range.output

diff --git a/README.md b/README.md
index 54c2e71b..071ca408 100644
--- a/README.md
+++ b/README.md
@@ -400,7 +400,7 @@ $ make
 $ ./json_unit "*"
 
 ===============================================================================
-All tests passed (3341006 assertions in 22 test cases)
+All tests passed (3341759 assertions in 27 test cases)
 ```
 
 For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/doc/examples/insert.cpp b/doc/examples/insert.cpp
new file mode 100644
index 00000000..c7adfc5a
--- /dev/null
+++ b/doc/examples/insert.cpp
@@ -0,0 +1,16 @@
+#include <json.hpp>
+
+using namespace nlohmann;
+
+int main()
+{
+    // create a JSON array
+    json v = {1, 2, 3, 4};
+
+    // insert number 10 before number 3
+    auto new_pos = v.insert(v.begin() + 2, 10);
+
+    // output new array and result of insert call
+    std::cout << *new_pos << '\n';
+    std::cout << v << '\n';
+}
diff --git a/doc/examples/insert.link b/doc/examples/insert.link
new file mode 100644
index 00000000..80ec801c
--- /dev/null
+++ b/doc/examples/insert.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/s0562du3uk9eMpos"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert.output b/doc/examples/insert.output
new file mode 100644
index 00000000..ed5cab1d
--- /dev/null
+++ b/doc/examples/insert.output
@@ -0,0 +1,2 @@
+10
+[1,2,10,3,4]
diff --git a/doc/examples/insert__count.cpp b/doc/examples/insert__count.cpp
new file mode 100644
index 00000000..cfdb39b6
--- /dev/null
+++ b/doc/examples/insert__count.cpp
@@ -0,0 +1,16 @@
+#include <json.hpp>
+
+using namespace nlohmann;
+
+int main()
+{
+    // create a JSON array
+    json v = {1, 2, 3, 4};
+
+    // insert number 7 copies of number 7 before number 3
+    auto new_pos = v.insert(v.begin() + 2, 7, 7);
+
+    // output new array and result of insert call
+    std::cout << *new_pos << '\n';
+    std::cout << v << '\n';
+}
diff --git a/doc/examples/insert__count.link b/doc/examples/insert__count.link
new file mode 100644
index 00000000..3c0e781c
--- /dev/null
+++ b/doc/examples/insert__count.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/kZCcDOrWRsmOCjOn"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert__count.output b/doc/examples/insert__count.output
new file mode 100644
index 00000000..294685ac
--- /dev/null
+++ b/doc/examples/insert__count.output
@@ -0,0 +1,2 @@
+7
+[1,2,7,7,7,7,7,7,7,3,4]
diff --git a/doc/examples/insert__range.cpp b/doc/examples/insert__range.cpp
new file mode 100644
index 00000000..03ea0bc3
--- /dev/null
+++ b/doc/examples/insert__range.cpp
@@ -0,0 +1,19 @@
+#include <json.hpp>
+
+using namespace nlohmann;
+
+int main()
+{
+    // create a JSON array
+    json v = {1, 2, 3, 4};
+
+    // create a JSON array to copy values from
+    json v2 = {"one", "two", "three", "four"};
+
+    // insert range from v2 before the end of array v
+    auto new_pos = v.insert(v.end(), v2.begin(), v2.end());
+
+    // output new array and result of insert call
+    std::cout << *new_pos << '\n';
+    std::cout << v << '\n';
+}
diff --git a/doc/examples/insert__range.link b/doc/examples/insert__range.link
new file mode 100644
index 00000000..03c5d012
--- /dev/null
+++ b/doc/examples/insert__range.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/lZeC91nMjP3Npmx7"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert__range.output b/doc/examples/insert__range.output
new file mode 100644
index 00000000..d50e9f63
--- /dev/null
+++ b/doc/examples/insert__range.output
@@ -0,0 +1,2 @@
+"one"
+[1,2,3,4,"one","two","three","four"]
diff --git a/src/json.hpp b/src/json.hpp
index cc3617c5..344d5f7b 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -3694,7 +3694,7 @@ class basic_json
     called on a JSON null value, an empty object is created before inserting @a
     value.
 
-    @param value the value to add to the JSON object
+    @param[in] value the value to add to the JSON object
 
     @throw std::domain_error when called on a type other than JSON object or
     null
@@ -3734,6 +3734,148 @@ class basic_json
         return operator[](value.first);
     }
 
+    /*!
+    @brief inserts element
+
+    Inserts element @a value before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] value element to insert
+    @return iterator pointing to the inserted @a value.
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+
+    @complexity Constant plus linear in the distance between pos and end of the
+    container.
+
+    @liveexample{The example shows how insert is used.,insert}
+    */
+    iterator insert(const_iterator pos, const basic_json& value)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, value);
+        return result;
+    }
+
+    /*!
+    @brief inserts element
+    @copydoc insert(const_iterator, const basic_json&)
+    */
+    iterator insert(const_iterator pos, basic_json&& value)
+    {
+        return insert(pos, value);
+    }
+
+    /*!
+    @brief inserts elements
+
+    Inserts @a count copies of @a value before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] count number of copies of @a value to insert
+    @param[in] value element to insert
+    @return iterator pointing to the first element inserted, or @a pos if
+    `count==0`
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+
+    @complexity Linear in @a count plus linear in the distance between @a pos
+    and end of the container.
+
+    @liveexample{The example shows how insert is used.,insert__count}
+    */
+    iterator insert(const_iterator pos, size_type count, const basic_json& value)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, count, value);
+        return result;
+    }
+
+    /*!
+    @brief inserts elements
+
+    Inserts elements from range `[first, last)` before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] first begin of the range of elements to insert
+    @param[in] last end of the range of elements to insert
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+    @throw std::domain_error if @a first and @a last do not belong to the same
+    JSON value
+    @throw std::domain_error if @a first or @a last are iterators into
+    container for which insert is called
+    @return iterator pointing to the first element inserted, or @a pos if
+    `first==last`
+
+    @complexity Linear in `std::distance(first, last)` plus linear in the
+    distance between @a pos and end of the container.
+
+    @liveexample{The example shows how insert is used.,insert__range}
+    */
+    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        if (first.m_object != last.m_object)
+        {
+            throw std::domain_error("iterators does not fit");
+        }
+
+        if (first.m_object == this or last.m_object == this)
+        {
+            throw std::domain_error("passed iterators may not belong to container");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator,
+                                     first.m_it.array_iterator, last.m_it.array_iterator);
+        return result;
+    }
+
     /*!
     @brief exchanges the values
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 58666091..e7624ea6 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -3694,7 +3694,7 @@ class basic_json
     called on a JSON null value, an empty object is created before inserting @a
     value.
 
-    @param value the value to add to the JSON object
+    @param[in] value the value to add to the JSON object
 
     @throw std::domain_error when called on a type other than JSON object or
     null
@@ -3734,6 +3734,148 @@ class basic_json
         return operator[](value.first);
     }
 
+    /*!
+    @brief inserts element
+
+    Inserts element @a value before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] value element to insert
+    @return iterator pointing to the inserted @a value.
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+
+    @complexity Constant plus linear in the distance between pos and end of the
+    container.
+
+    @liveexample{The example shows how insert is used.,insert}
+    */
+    iterator insert(const_iterator pos, const basic_json& value)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, value);
+        return result;
+    }
+
+    /*!
+    @brief inserts element
+    @copydoc insert(const_iterator, const basic_json&)
+    */
+    iterator insert(const_iterator pos, basic_json&& value)
+    {
+        return insert(pos, value);
+    }
+
+    /*!
+    @brief inserts elements
+
+    Inserts @a count copies of @a value before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] count number of copies of @a value to insert
+    @param[in] value element to insert
+    @return iterator pointing to the first element inserted, or @a pos if
+    `count==0`
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+
+    @complexity Linear in @a count plus linear in the distance between @a pos
+    and end of the container.
+
+    @liveexample{The example shows how insert is used.,insert__count}
+    */
+    iterator insert(const_iterator pos, size_type count, const basic_json& value)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, count, value);
+        return result;
+    }
+
+    /*!
+    @brief inserts elements
+
+    Inserts elements from range `[first, last)` before iterator @a pos.
+
+    @param[in] pos iterator before which the content will be inserted; may be
+    the end() iterator
+    @param[in] first begin of the range of elements to insert
+    @param[in] last end of the range of elements to insert
+
+    @throw std::domain_error if called on JSON values other than arrays
+    @throw std::domain_error if @a pos is not an iterator of *this
+    @throw std::domain_error if @a first and @a last do not belong to the same
+    JSON value
+    @throw std::domain_error if @a first or @a last are iterators into
+    container for which insert is called
+    @return iterator pointing to the first element inserted, or @a pos if
+    `first==last`
+
+    @complexity Linear in `std::distance(first, last)` plus linear in the
+    distance between @a pos and end of the container.
+
+    @liveexample{The example shows how insert is used.,insert__range}
+    */
+    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+    {
+        // insert only works for arrays
+        if (m_type != value_t::array)
+        {
+            throw std::domain_error("cannot use insert() with " + type_name());
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (pos.m_object != this)
+        {
+            throw std::domain_error("iterator does not fit current value");
+        }
+
+        if (first.m_object != last.m_object)
+        {
+            throw std::domain_error("iterators does not fit");
+        }
+
+        if (first.m_object == this or last.m_object == this)
+        {
+            throw std::domain_error("passed iterators may not belong to container");
+        }
+
+        // insert to array and return iterator
+        iterator result(this);
+        result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator,
+                                     first.m_it.array_iterator, last.m_it.array_iterator);
+        return result;
+    }
+
     /*!
     @brief exchanges the values
 
diff --git a/test/unit.cpp b/test/unit.cpp
index 6f5f18f9..3e721627 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -6608,6 +6608,162 @@ TEST_CASE("modifiers")
         }
     }
 
+    SECTION("insert")
+    {
+        json j_array = {1, 2, 3, 4};
+        json j_value = 5;
+
+        SECTION("value at position")
+        {
+            SECTION("insert before begin()")
+            {
+                auto it = j_array.insert(j_array.begin(), j_value);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK(j_array.begin() == it);
+                CHECK(j_array == json({5, 1, 2, 3, 4}));
+            }
+
+            SECTION("insert in the middle")
+            {
+                auto it = j_array.insert(j_array.begin() + 2, j_value);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK((it - j_array.begin()) == 2);
+                CHECK(j_array == json({1, 2, 5, 3, 4}));
+            }
+
+            SECTION("insert before end()")
+            {
+                auto it = j_array.insert(j_array.end(), j_value);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK((j_array.end() - it) == 1);
+                CHECK(j_array == json({1, 2, 3, 4, 5}));
+            }
+        }
+
+        SECTION("rvalue at position")
+        {
+            SECTION("insert before begin()")
+            {
+                auto it = j_array.insert(j_array.begin(), 5);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK(j_array.begin() == it);
+                CHECK(j_array == json({5, 1, 2, 3, 4}));
+            }
+
+            SECTION("insert in the middle")
+            {
+                auto it = j_array.insert(j_array.begin() + 2, 5);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK((it - j_array.begin()) == 2);
+                CHECK(j_array == json({1, 2, 5, 3, 4}));
+            }
+
+            SECTION("insert before end()")
+            {
+                auto it = j_array.insert(j_array.end(), 5);
+                CHECK(j_array.size() == 5);
+                CHECK(*it == j_value);
+                CHECK((j_array.end() - it) == 1);
+                CHECK(j_array == json({1, 2, 3, 4, 5}));
+            }
+        }
+
+        SECTION("copies at position")
+        {
+            SECTION("insert before begin()")
+            {
+                auto it = j_array.insert(j_array.begin(), 3, 5);
+                CHECK(j_array.size() == 7);
+                CHECK(*it == j_value);
+                CHECK(j_array.begin() == it);
+                CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4}));
+            }
+
+            SECTION("insert in the middle")
+            {
+                auto it = j_array.insert(j_array.begin() + 2, 3, 5);
+                CHECK(j_array.size() == 7);
+                CHECK(*it == j_value);
+                CHECK((it - j_array.begin()) == 2);
+                CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4}));
+            }
+
+            SECTION("insert before end()")
+            {
+                auto it = j_array.insert(j_array.end(), 3, 5);
+                CHECK(j_array.size() == 7);
+                CHECK(*it == j_value);
+                CHECK((j_array.end() - it) == 3);
+                CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5}));
+            }
+
+            SECTION("insert nothing (count = 0)")
+            {
+                auto pos = j_array.end();
+                auto it = j_array.insert(j_array.end(), 0, 5);
+                CHECK(j_array.size() == 4);
+                CHECK(it == pos);
+                CHECK(j_array == json({1, 2, 3, 4}));
+            }
+        }
+
+        SECTION("range")
+        {
+            json j_other_array = {"first", "second"};
+
+            SECTION("proper usage")
+            {
+                auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
+                CHECK(j_array.size() == 6);
+                CHECK(*it == *j_other_array.begin());
+                CHECK((j_array.end() - it) == 2);
+                CHECK(j_array == json({1, 2, 3, 4, "first", "second"}));
+            }
+
+            SECTION("empty range")
+            {
+                auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
+                CHECK(j_array.size() == 4);
+                CHECK(it == j_array.end());
+                CHECK(j_array == json({1, 2, 3, 4}));
+            }
+
+            SECTION("invalid iterators")
+            {
+                CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error);
+            }
+        }
+
+        SECTION("invalid iterator")
+        {
+            // pass iterator to a different array
+            json j_another_array = {1, 2};
+            json j_yet_another_array = {"first", "second"};
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(),
+                                           j_yet_another_array.end()), std::domain_error);
+        }
+
+        SECTION("non-array type")
+        {
+            // call insert on a non-array type
+            json j_nonarray = 3;
+            json j_yet_another_array = {"first", "second"};
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
+                                              j_yet_another_array.end()), std::domain_error);
+        }
+    }
+
     SECTION("swap()")
     {
         SECTION("json")