Repository: nifi-minifi-cpp
Updated Branches:
  refs/heads/master 342cee09a -> d4f0bcfd3


http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/generate/ostream.hpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/include/bustache/generate/ostream.hpp 
b/thirdparty/bustache/include/bustache/generate/ostream.hpp
new file mode 100644
index 0000000..8f1a83e
--- /dev/null
+++ b/thirdparty/bustache/include/bustache/generate/ostream.hpp
@@ -0,0 +1,60 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2016 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#ifndef BUSTACHE_GENERATE_OSTREAM_HPP_INCLUDED
+#define BUSTACHE_GENERATE_OSTREAM_HPP_INCLUDED
+
+#include <iostream>
+#include <bustache/generate.hpp>
+
+namespace bustache { namespace detail
+{
+    template<class CharT, class Traits>
+    struct ostream_sink
+    {
+        std::basic_ostream<CharT, Traits>& out;
+
+        void operator()(char const* it, char const* end) const
+        {
+            out.write(it, end - it);
+        }
+
+        template<class T>
+        void operator()(T data) const
+        {
+            out << data;
+        }
+
+        void operator()(bool data) const
+        {
+            out << (data ? "true" : "false");
+        }
+    };
+}}
+
+namespace bustache
+{
+    template<class CharT, class Traits, class Context>
+    void generate_ostream
+    (
+        std::basic_ostream<CharT, Traits>& out, format const& fmt,
+        value::view const& data, Context const& context, option_type flag
+    )
+    {
+        detail::ostream_sink<CharT, Traits> sink{out};
+        generate(sink, fmt, data, context, flag);
+    }
+
+    // This is instantiated in src/generate.cpp.
+    extern template
+    void generate_ostream
+    (
+        std::ostream& out, format const& fmt,
+        value::view const& data, detail::any_context const& context, 
option_type flag
+    );
+}
+
+#endif
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/generate/string.hpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/include/bustache/generate/string.hpp 
b/thirdparty/bustache/include/bustache/generate/string.hpp
new file mode 100644
index 0000000..cd1ddb4
--- /dev/null
+++ b/thirdparty/bustache/include/bustache/generate/string.hpp
@@ -0,0 +1,108 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2016 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#ifndef BUSTACHE_GENERATE_STRING_HPP_INCLUDED
+#define BUSTACHE_GENERATE_STRING_HPP_INCLUDED
+
+#include <cstdio> // for snprintf
+#include <string>
+#include <bustache/generate.hpp>
+
+namespace bustache { namespace detail
+{
+    template<class String>
+    struct string_sink
+    {
+        String& out;
+
+        void operator()(char const* it, char const* end) const
+        {
+            out.insert(out.end(), it, end);
+        }
+
+        void operator()(int data) const
+        {
+            append_num("%d", data);
+        }
+
+        void operator()(double data) const
+        {
+            append_num("%g", data);
+        }
+
+        void operator()(bool data) const
+        {
+            data ? append("true") : append("false");
+        }
+
+        template<std::size_t N>
+        void append(char const (&str)[N]) const
+        {
+            out.insert(out.end(), str, str + (N - 1));
+        }
+
+        template<class T>
+        void append_num(char const* fmt, T data) const
+        {
+            char buf[64];
+            char* p;
+            auto old_size = out.size();
+            auto capacity = out.capacity();
+            auto bufsize = capacity - old_size;
+            if (bufsize)
+            {
+                out.resize(capacity);
+                p = &out.front() + old_size;
+            }
+            else
+            {
+                bufsize = sizeof(buf);
+                p = buf;
+            }
+            auto n = std::snprintf(p, bufsize, fmt, data);
+            if (n < 0) // error
+                return;
+            if (unsigned(n + 1) <= bufsize)
+            {
+                if (p == buf)
+                {
+                    out.insert(out.end(), p, p + n);
+                    return;
+                }
+            }
+            else
+            {
+                out.resize(old_size + n + 1); // '\0' will be written
+                std::snprintf(&out.front() + old_size, n + 1, fmt, data);
+            }
+            out.resize(old_size + n);
+        }
+    };
+}}
+
+namespace bustache
+{
+    template<class String, class Context>
+    void generate_string
+    (
+        String& out, format const& fmt,
+        value::view const& data, Context const& context, option_type flag
+    )
+    {
+        detail::string_sink<String> sink{out};
+        generate(sink, fmt, data, context, flag);
+    }
+
+    // This is instantiated in src/generate.cpp.
+    extern template
+    void generate_string
+    (
+        std::string& out, format const& fmt,
+        value::view const& data, detail::any_context const& context, 
option_type flag
+    );
+}
+
+#endif
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/model.hpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/include/bustache/model.hpp 
b/thirdparty/bustache/include/bustache/model.hpp
new file mode 100644
index 0000000..87b7a28
--- /dev/null
+++ b/thirdparty/bustache/include/bustache/model.hpp
@@ -0,0 +1,165 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2014-2017 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#ifndef BUSTACHE_MODEL_HPP_INCLUDED
+#define BUSTACHE_MODEL_HPP_INCLUDED
+
+#include <bustache/format.hpp>
+#include <bustache/detail/variant.hpp>
+#include <bustache/detail/any_context.hpp>
+#include <vector>
+#include <functional>
+#include <boost/unordered_map.hpp>
+
+namespace bustache
+{
+    class value;
+
+    using array = std::vector<value>;
+
+    // We use boost::unordered_map because it allows incomplete type.
+    using object = boost::unordered_map<std::string, value>;
+
+    using lambda0v = std::function<value()>;
+
+    using lambda0f = std::function<format()>;
+
+    using lambda1v = std::function<value(ast::content_list const&)>;
+
+    using lambda1f = std::function<format(ast::content_list const&)>;
+
+    namespace detail
+    {
+        struct bool_
+        {
+            bool_(bool);
+        };
+    }
+
+#define BUSTACHE_VALUE(X, D)                                                   
 \
+    X(0, std::nullptr_t, D)                                                    
 \
+    X(1, bool, D)                                                              
 \
+    X(2, int, D)                                                               
 \
+    X(3, double, D)                                                            
 \
+    X(4, std::string, D)                                                       
 \
+    X(5, array, D)                                                             
 \
+    X(6, lambda0v, D)                                                          
 \
+    X(7, lambda0f, D)                                                          
 \
+    X(8, lambda1v, D)                                                          
 \
+    X(9, lambda1f, D)                                                          
 \
+    X(10, object, D)                                                           
 \
+/***/
+
+    class value : public variant_base<value>
+    {
+        static std::nullptr_t match_type(std::nullptr_t);
+        static int match_type(int);
+        // Use a fake bool_ to prevent unintended bool conversion.
+        static bool match_type(detail::bool_);
+        static double match_type(double);
+        static std::string match_type(std::string);
+        static array match_type(array);
+        static lambda0v match_type(lambda0v);
+        static lambda0f match_type(lambda0f);
+        static lambda1v match_type(lambda1v);
+        static lambda1f match_type(lambda1f);
+        static object match_type(object);
+        // Need to override for `char const*`, otherwise `bool` will be chosen
+        static std::string match_type(char const*);
+
+    public:
+
+        struct view;
+        using pointer = variant_ptr<view>;
+
+        Zz_BUSTACHE_VARIANT_DECL(value, BUSTACHE_VALUE, false)
+
+        value() noexcept : _which(0), _0() {}
+
+        pointer get_pointer() const
+        {
+            return {_which, _storage};
+        }
+    };
+
+    struct value::view : variant_base<view>
+    {
+        using switcher = value::switcher;
+
+#define BUSTACHE_VALUE_VIEW_CTOR(N, U, D)                                      
 \
+        view(U const& data) noexcept : _which(N), _data(&data) {}
+        BUSTACHE_VALUE(BUSTACHE_VALUE_VIEW_CTOR,)
+#undef BUSTACHE_VALUE_VIEW_CTOR
+
+        view(value const& data) noexcept
+          : _which(data._which), _data(data._storage)
+        {}
+
+        view(unsigned which, void const* data) noexcept
+          : _which(which), _data(data)
+        {}
+
+        unsigned which() const
+        {
+            return _which;
+        }
+
+        void const* data() const
+        {
+            return _data;
+        }
+
+        pointer get_pointer() const
+        {
+            return {_which, _data};
+        }
+
+    private:
+
+        unsigned _which;
+        void const* _data;
+    };
+#undef BUSTACHE_VALUE
+}
+
+namespace bustache
+{
+    // Forward decl only.
+    template<class CharT, class Traits, class Context>
+    void generate_ostream
+    (
+        std::basic_ostream<CharT, Traits>& out, format const& fmt,
+        value::view const& data, Context const& context, option_type flag
+    );
+
+    // Forward decl only.
+    template<class String, class Context>
+    void generate_string
+    (
+        String& out, format const& fmt,
+        value::view const& data, Context const& context, option_type flag
+    );
+
+    template<class CharT, class Traits, class T, class Context,
+        typename std::enable_if<std::is_constructible<value::view, T>::value, 
bool>::type = true>
+    inline std::basic_ostream<CharT, Traits>&
+    operator<<(std::basic_ostream<CharT, Traits>& out, manipulator<T, Context> 
const& manip)
+    {
+        generate_ostream(out, manip.fmt, manip.data, 
detail::any_context(manip.context), manip.flag);
+        return out;
+    }
+
+    template<class T, class Context,
+        typename std::enable_if<std::is_constructible<value::view, T>::value, 
bool>::type = true>
+    inline std::string to_string(manipulator<T, Context> const& manip)
+    {
+        std::string ret;
+        generate_string(ret, manip.fmt, manip.data, 
detail::any_context(manip.context), manip.flag);
+        return ret;
+    }
+}
+
+#endif
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/src/format.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/src/format.cpp 
b/thirdparty/bustache/src/format.cpp
new file mode 100644
index 0000000..7b56084
--- /dev/null
+++ b/thirdparty/bustache/src/format.cpp
@@ -0,0 +1,484 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2014-2016 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#include <cctype>
+#include <utility>
+#include <cstring>
+#include <bustache/format.hpp>
+
+namespace bustache { namespace parser { namespace
+{
+    using delim = std::pair<std::string, std::string>;
+
+    template<class I>
+    inline void skip(I& i, I e)
+    {
+        while (i != e && std::isspace(*i))
+            ++i;
+    }
+
+    template<class I>
+    inline bool parse_char(I& i, I e, char c)
+    {
+        if (i != e && *i == c)
+        {
+            ++i;
+            return true;
+        }
+        return false;
+    }
+
+    template<class I>
+    inline bool parse_lit(I& i, I e, boost::string_ref const& str)
+    {
+        I i0 = i;
+        for (char c : str)
+        {
+            if (!parse_char(i, e, c))
+            {
+                i = i0;
+                return false;
+            }
+        }
+        return true;
+    }
+
+    template<class I>
+    void expect_key(I& i, I e, delim& d, std::string& attr, bool suffix)
+    {
+        skip(i, e);
+        I i0 = i;
+        while (i != e)
+        {
+            I i1 = i;
+            skip(i, e);
+            if (!suffix || parse_char(i, e, '}'))
+            {
+                skip(i, e);
+                if (parse_lit(i, e, d.second))
+                {
+                    attr.assign(i0, i1);
+                    if (i0 == i1)
+                        throw format_error(error_badkey);
+                    return;
+                }
+            }
+            if (i != e)
+                ++i;
+        }
+        throw format_error(error_badkey);
+    }
+
+    template<class I>
+    bool parse_content
+    (
+        I& i0, I& i, I e, delim& d, bool& pure,
+        boost::string_ref& text, ast::content& attr,
+        boost::string_ref const& section
+    );
+
+    template<class I>
+    void parse_contents
+    (
+        I i0, I& i, I e, delim& d, bool& pure,
+        ast::content_list& attr, boost::string_ref const& section
+    );
+
+    template<class I>
+    I process_pure(I& i, I e, bool& pure)
+    {
+        I i0 = i;
+        if (pure)
+        {
+            while (i != e)
+            {
+                if (*i == '\n')
+                {
+                    i0 = ++i;
+                    break;
+                }
+                else if (std::isspace(*i))
+                    ++i;
+                else
+                {
+                    pure = false;
+                    break;
+                }
+            }
+        }
+        return i0;
+    }
+
+    template<class I>
+    inline bool expect_block(I& i, I e, delim& d, bool& pure, ast::block& attr)
+    {
+        expect_key(i, e, d, attr.key, false);
+        I i0 = process_pure(i, e, pure);
+        bool standalone = pure;
+        parse_contents(i0, i, e, d, pure, attr.contents, attr.key);
+        return standalone;
+    }
+
+    template<class I>
+    bool expect_inheritance(I& i, I e, delim& d, bool& pure, ast::partial& 
attr)
+    {
+        expect_key(i, e, d, attr.key, false);
+        I i0 = process_pure(i, e, pure);
+        bool standalone = pure;
+        for (boost::string_ref text;;)
+        {
+            ast::content a;
+            auto end = parse_content(i0, i, e, d, pure, text, a, attr.key);
+            if (auto p = get<ast::block>(&a))
+                attr.overriders.emplace(std::move(p->key), 
std::move(p->contents));
+            if (end)
+                break;
+        }
+        return standalone;
+    }
+
+    template<class I>
+    void expect_comment(I& i, I e, delim& d)
+    {
+        while (!parse_lit(i, e, d.second))
+        {
+            if (i == e)
+                throw format_error(error_delim);
+            ++i;
+        }
+    }
+
+    template<class I>
+    void expect_set_delim(I& i, I e, delim& d)
+    {
+        skip(i, e);
+        I i0 = i;
+        while (i != e)
+        {
+            if (std::isspace(*i))
+                break;
+            ++i;
+        }
+        if (i == e)
+            throw format_error(error_baddelim);
+        d.first.assign(i0, i);
+        skip(i, e);
+        i0 = i;
+        I i1 = i;
+        for (;; ++i)
+        {
+            if (i == e)
+                throw format_error(error_set_delim);
+            if (*i == '=')
+            {
+                i1 = i;
+                break;
+            }
+            if (std::isspace(*i))
+            {
+                i1 = i;
+                skip(++i, e);
+                if (i == e || *i != '=')
+                    throw format_error(error_set_delim);
+                break;
+            }
+        }
+        if (i0 == i1)
+            throw format_error(error_baddelim);
+        std::string new_close(i0, i1);
+        skip(++i, e);
+        if (!parse_lit(i, e, d.second))
+            throw format_error(error_delim);
+        d.second = std::move(new_close);
+    }
+
+    struct tag_result
+    {
+        bool is_end_section;
+        bool check_standalone;
+        bool is_standalone;
+    };
+
+    template<class I>
+    tag_result expect_tag
+    (
+        I& i, I e, delim& d, bool& pure,
+        ast::content& attr, boost::string_ref const& section
+    )
+    {
+        skip(i, e);
+        if (i == e)
+            throw format_error(error_badkey);
+        tag_result ret{};
+        switch (*i)
+        {
+        case '#':
+        case '^':
+        {
+            ast::section a;
+            a.tag = *i;
+            ret.is_standalone = expect_block(++i, e, d, pure, a);
+            attr = std::move(a);
+            return ret;
+        }
+        case '/':
+            skip(++i, e);
+            if (section.empty() || !parse_lit(i, e, section))
+                throw format_error(error_section);
+            skip(i, e);
+            if (!parse_lit(i, e, d.second))
+                throw format_error(error_delim);
+            ret.check_standalone = pure;
+            ret.is_end_section = true;
+            break;
+        case '!':
+        {
+            expect_comment(++i, e, d);
+            ret.check_standalone = pure;
+            break;
+        }
+        case '=':
+        {
+            expect_set_delim(++i, e, d);
+            ret.check_standalone = pure;
+            break;
+        }
+        case '>':
+        {
+            ast::partial a;
+            expect_key(++i, e, d, a.key, false);
+            attr = std::move(a);
+            ret.check_standalone = pure;
+            break;
+        }
+        case '&':
+        case '{':
+        {
+            ast::variable a;
+            a.tag = *i;
+            expect_key(++i, e, d, a.key, a.tag == '{');
+            attr = std::move(a);
+            pure = false;
+            break;
+        }
+        // Extensions
+        case '<':
+        {
+            ast::partial a;
+            ret.is_standalone = expect_inheritance(++i, e, d, pure, a);
+            attr = std::move(a);
+            return ret;
+        }
+        case '$':
+        {
+            ast::block a;
+            ret.is_standalone = expect_block(++i, e, d, pure, a);
+            attr = std::move(a);
+            return ret;
+        }
+        default:
+            ast::variable a;
+            expect_key(i, e, d, a.key, false);
+            attr = std::move(a);
+            pure = false;
+            break;
+        }
+        return ret;
+    }
+
+    // return true if it ends
+    template<class I>
+    bool parse_content
+    (
+        I& i0, I& i, I e, delim& d, bool& pure,
+        boost::string_ref& text, ast::content& attr,
+        boost::string_ref const& section
+    )
+    {
+        for (I i1 = i; i != e;)
+        {
+            if (*i == '\n')
+            {
+                pure = true;
+                i1 = ++i;
+            }
+            else if (std::isspace(*i))
+                ++i;
+            else
+            {
+                I i2 = i;
+                if (parse_lit(i, e, d.first))
+                {
+                    tag_result tag(expect_tag(i, e, d, pure, attr, section));
+                    text = boost::string_ref(i0, i1 - i0);
+                    if (tag.check_standalone)
+                    {
+                        I i3 = i;
+                        while (i != e)
+                        {
+                            if (*i == '\n')
+                            {
+                                ++i;
+                                break;
+                            }
+                            else if (std::isspace(*i))
+                                ++i;
+                            else
+                            {
+                                pure = false;
+                                text = boost::string_ref(i0, i2 - i0);
+                                // For end-section, we move the current pos (i)
+                                // since i0 is local to the section and is not
+                                // propagated upwards.
+                                (tag.is_end_section ? i : i0) = i3;
+                                return tag.is_end_section;
+                            }
+                        }
+                        tag.is_standalone = true;
+                    }
+                    if (!tag.is_standalone)
+                        text = boost::string_ref(i0, i2 - i0);
+                    else if (auto partial = get<ast::partial>(&attr))
+                        partial->indent.assign(i1, i2 - i1);
+                    i0 = i;
+                    return i == e || tag.is_end_section;
+                }
+                else
+                {
+                    pure = false;
+                    ++i;
+                }
+            }
+        }
+        text = boost::string_ref(i0, i - i0);
+        return true;
+    }
+
+    template<class I>
+    void parse_contents
+    (
+        I i0, I& i, I e, delim& d, bool& pure,
+        ast::content_list& attr, boost::string_ref const& section
+    )
+    {
+        for (;;)
+        {
+            boost::string_ref text;
+            ast::content a;
+            auto end = parse_content(i0, i, e, d, pure, text, a, section);
+            if (!text.empty())
+                attr.push_back(text);
+            if (!is_null(a))
+                attr.push_back(std::move(a));
+            if (end)
+                return;
+        }
+    }
+
+    template<class I>
+    inline void parse_start(I& i, I e, ast::content_list& attr)
+    {
+        delim d("{{", "}}");
+        bool pure = true;
+        parse_contents(i, i, e, d, pure, attr, {});
+    }
+}}}
+
+namespace bustache
+{
+    static char const* get_error_string(error_type err)
+    {
+        switch (err)
+        {
+        case error_set_delim:
+            return "format_error(error_set_delim): mismatched '='";
+        case error_baddelim:
+            return "format_error(error_baddelim): invalid delimiter";
+        case error_delim:
+            return "format_error(error_delim): mismatched delimiter";
+        case error_section:
+            return "format_error(error_section): mismatched end section tag";
+        case error_badkey:
+            return "format_error(error_badkey): invalid key";
+        default:
+            return "format_error";
+        }
+    }
+
+    format_error::format_error(error_type err)
+      : runtime_error(get_error_string(err)), _err(err)
+    {}
+
+    void format::init(char const* begin, char const* end)
+    {
+        parser::parse_start(begin, end, _contents);
+    }
+
+    struct accum_size
+    {
+        using result_type = std::size_t;
+
+        std::size_t operator()(ast::text const& text) const
+        {
+            return text.size();
+        }
+
+        std::size_t operator()(ast::section const& section) const
+        {
+            std::size_t n = 0;
+            for (auto const& content : section.contents)
+                n += visit(*this, content);
+            return n;
+        }
+
+        template <typename T>
+        std::size_t operator()(T const&) const
+        {
+            return 0;
+        }
+    };
+
+    std::size_t format::text_size() const
+    {
+        accum_size accum;
+        std::size_t n = 0;
+        for (auto const& content : _contents)
+            n += visit(accum, content);
+        return n;
+    }
+
+    struct copy_text_visitor
+    {
+        using result_type = void;
+
+        char* data;
+
+        void operator()(ast::text& text)
+        {
+            auto n = text.size();
+            std::memcpy(data, text.data(), n);
+            text = {data, n};
+            data += n;
+        }
+
+        void operator()(ast::section& section)
+        {
+            for (auto& content : section.contents)
+                visit(*this, content);
+        }
+
+        template <typename T>
+        void operator()(T const&) const {}
+    };
+
+    void format::copy_text(std::size_t n)
+    {
+        _text.reset(new char[n]);
+        copy_text_visitor visitor{_text.get()};
+        for (auto& content : _contents)
+            visit(visitor, content);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/src/generate.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/src/generate.cpp 
b/thirdparty/bustache/src/generate.cpp
new file mode 100644
index 0000000..1e41455
--- /dev/null
+++ b/thirdparty/bustache/src/generate.cpp
@@ -0,0 +1,77 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2016 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+
+#include <bustache/generate.hpp>
+#include <bustache/generate/ostream.hpp>
+#include <bustache/generate/string.hpp>
+
+namespace bustache { namespace detail
+{
+    value::pointer content_visitor_base::resolve(std::string const& key) const
+    {
+        auto ki = key.begin();
+        auto ke = key.end();
+        if (ki == ke)
+            return{};
+        value::pointer pv = nullptr;
+        if (*ki == '.')
+        {
+            if (++ki == ke)
+                return cursor;
+            auto k0 = ki;
+            while (*ki != '.' && ++ki != ke);
+            key_cache.assign(k0, ki);
+            pv = find(scope->data, key_cache);
+        }
+        else
+        {
+            auto k0 = ki;
+            while (ki != ke && *ki != '.') ++ki;
+            key_cache.assign(k0, ki);
+            pv = scope->lookup(key_cache);
+        }
+        if (ki == ke)
+            return pv;
+        if (auto obj = get<object>(pv))
+        {
+            auto k0 = ++ki;
+            while (ki != ke)
+            {
+                if (*ki == '.')
+                {
+                    key_cache.assign(k0, ki);
+                    obj = get<object>(find(*obj, key_cache));
+                    if (!obj)
+                        return nullptr;
+                    k0 = ++ki;
+                }
+                else
+                    ++ki;
+            }
+            key_cache.assign(k0, ki);
+            return find(*obj, key_cache);
+        }
+        return nullptr;
+    }
+}}
+
+namespace bustache
+{
+    template
+    void generate_ostream
+    (
+        std::ostream& out, format const& fmt,
+        value::view const& data, detail::any_context const& context, 
option_type flag
+    );
+
+    template
+    void generate_string
+    (
+        std::string& out, format const& fmt,
+        value::view const& data, detail::any_context const& context, 
option_type flag
+    );
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/test/CMakeLists.txt 
b/thirdparty/bustache/test/CMakeLists.txt
new file mode 100644
index 0000000..74ecaee
--- /dev/null
+++ b/thirdparty/bustache/test/CMakeLists.txt
@@ -0,0 +1,22 @@
+find_package(Catch REQUIRED)
+
+add_library(Catch INTERFACE)
+target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
+
+function(add_catch_test name)
+    set(TEST_TARGET test_${name})
+    add_executable(${TEST_TARGET}
+        ${name}.cpp
+    )
+    target_link_libraries(${TEST_TARGET}
+        ${PROJECT_NAME} Catch
+    )
+    set_target_properties(${TEST_TARGET} PROPERTIES
+        CXX_STANDARD 14
+    )
+    add_test(${TEST_TARGET} ${TEST_TARGET})
+endfunction()
+
+add_catch_test(specs)
+
+add_catch_test(variant)

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/benchmark.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/test/benchmark.cpp 
b/thirdparty/bustache/test/benchmark.cpp
new file mode 100644
index 0000000..d9b434c
--- /dev/null
+++ b/thirdparty/bustache/test/benchmark.cpp
@@ -0,0 +1,238 @@
+#include <benchmark/benchmark.h>
+#include <bustache/model.hpp>
+#include <mstch/mstch.hpp>
+#include <mustache.hpp>
+
+static char tmp[] =
+R"(<h1>{{header}}</h1>
+{{#bug}}
+{{/bug}}
+
+{{# items}}
+  {{#first}}
+    <li><strong>{{name}}</strong></li>
+  {{/first}}
+  {{#link}}
+    <li><a {{>href}}>{{name}}</a></li>
+  {{/link}}
+{{ /items}}
+
+{{#empty}}
+  <p>The list is empty.</p>
+{{/ empty }}
+
+{{=[ ]=}}
+
+[#array]([.])[/array]
+
+[#items]
+[count]->[count]->[count]
+[/items]
+
+[a.b.c] == [#a][#b][c][/b][/a]
+
+<div class="comments">
+    <h3>[header]</h3>
+    <ul>
+        [#comments]
+        <li class="comment">
+            <h5>[name]</h5>
+            <p>[body]</p>
+        </li>
+        <!--[count]-->
+        [/comments]
+    </ul>
+</div>)";
+
+static void bustache_usage(benchmark::State& state)
+{
+    using namespace bustache;
+
+    boost::unordered_map<std::string, bustache::format> context
+    {
+        {"href", "href=\"{{url}}\""_fmt}
+    };
+
+    int n = 0;
+    object data
+    {
+        {"header", "Colors"},
+        {"items",
+            array
+            {
+                object
+                {
+                    {"name", "red"},
+                    {"first", true},
+                    {"url", "#Red"}
+                },
+                object
+                {
+                    {"name", "green"},
+                    {"link", true},
+                    {"url", "#Green"}
+                },
+                object
+                {
+                    {"name", "blue"},
+                    {"link", true},
+                    {"url", "#Blue"}
+                }
+            }
+        },
+        {"empty", false},
+        {"count", [&n] { return ++n; }},
+        {"array", array{1, 2, 3}},
+        {"a", object{{"b", object{{"c", true}}}}},
+        {"comments",
+            array
+            {
+                object
+                {
+                    {"name", "Joe"},
+                    {"body", "<html> should be escaped"}
+                },
+                object
+                {
+                    {"name", "Sam"},
+                    {"body", "{{mustache}} can be seen"}
+                },
+                object
+                {
+                    {"name", "New"},
+                    {"body", "break\nup"}
+                }
+            }
+        }
+    };
+
+    format fmt(tmp);
+
+    while (state.KeepRunning())
+    {
+        n = 0;
+        to_string(fmt(data, context, escape_html));
+    }
+}
+
+static void mstch_usage(benchmark::State& state)
+{
+    using namespace mstch;
+    using namespace std::string_literals;
+
+    std::map<std::string, std::string> context
+    {
+        {"href", "href=\"{{url}}\""}
+    };
+
+    int n = 0;
+    map data
+    {
+        {"header", "Colors"s},
+        {"items",
+            array
+            {
+                map
+                {
+                    {"name", "red"s},
+                    {"first", true},
+                    {"url", "#Red"s}
+                },
+                map
+                {
+                    {"name", "green"s},
+                    {"link", true},
+                    {"url", "#Green"s}
+                },
+                map
+                {
+                    {"name", "blue"s},
+                    {"link", true},
+                    {"url", "#Blue"s}
+                }
+            }
+        },
+        {"empty", false},
+        {"count", lambda{[&n]() -> node { return ++n; }}},
+        {"array", array{1, 2, 3}},
+        {"a", map{{"b", map{{"c", true}}}}},
+        {"comments",
+            array
+            {
+                map
+                {
+                    {"name", "Joe"s},
+                    {"body", "<html> should be escaped"s}
+                },
+                map
+                {
+                    {"name", "Sam"s},
+                    {"body", "{{mustache}} can be seen"s}
+                },
+                map
+                {
+                    {"name", "New"s},
+                    {"body", "break\nup"s}
+                }
+            }
+        }
+    };
+
+    while (state.KeepRunning())
+    {
+        n = 0;
+        render(tmp, data, context);
+    }
+}
+
+static void kainjow_usage(benchmark::State& state)
+{
+    using namespace Kainjow;
+    using Data = Mustache::Data;
+
+    int n = 0;
+    Data data;
+    data.set("header", "Colors");
+    {
+        Data d1, d2, d3;
+        d1.set("name", "red");
+        d1.set("first", Data::Type::True);
+        d1.set("url", "#Red");
+        d2.set("name", "green");
+        d2.set("link", Data::Type::True);
+        d2.set("url", "#Green");
+        d3.set("name", "blue");
+        d3.set("link", Data::Type::True);
+        d3.set("url", "#Blue");
+        data.set("items", Data::ListType{d1, d2, d3});
+    }
+    data.set("empty", Data::Type::False);
+    data.set("count", Data::LambdaType{[&n](const std::string&) { return 
std::to_string(++n); }});
+    data.set("array", Data::ListType{"1", "2", "3"});
+    data.set("a", {"b",{"c", "true"}});
+    {
+        Data d1, d2, d3;
+        d1.set("name", "Joe");
+        d1.set("body", "<html> should be escaped");
+        d2.set("name", "Sam");
+        d2.set("body", "{{mustache}} can be seen");
+        d3.set("name", "New");
+        d3.set("body", "break\nup");
+        data.set("comments", Data::ListType{d1, d2, d3});
+    }
+    data.set("href", Data::PartialType{[]() { return "href=\"{{url}}\""; }});
+
+    Mustache fmt(tmp);
+
+    while (state.KeepRunning())
+    {
+        n = 0;
+        fmt.render(data);
+    }
+}
+
+BENCHMARK(bustache_usage);
+BENCHMARK(mstch_usage);
+BENCHMARK(kainjow_usage);
+
+BENCHMARK_MAIN();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/specs.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/test/specs.cpp 
b/thirdparty/bustache/test/specs.cpp
new file mode 100644
index 0000000..617ed1d
--- /dev/null
+++ b/thirdparty/bustache/test/specs.cpp
@@ -0,0 +1,714 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2016 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
+#include <bustache/model.hpp>
+#include <boost/unordered_map.hpp>
+
+using namespace bustache;
+using context = boost::unordered_map<std::string, format>;
+
+TEST_CASE("interpolation")
+{
+    object const empty;
+
+    // No Interpolation
+    CHECK(to_string("Hello from {Mustache}!"_fmt(empty)) == "Hello from 
{Mustache}!");
+
+    // Basic Interpolation
+    CHECK(to_string("Hello, {{subject}}!"_fmt(object{{"subject", "world"}})) 
== "Hello, world!");
+
+    // HTML Escaping
+    CHECK(to_string("These characters should be HTML escaped: 
{{forbidden}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html))
+        == "These characters should be HTML escaped: &amp; &quot; &lt; &gt;");
+
+    // Triple Mustache
+    CHECK(to_string("These characters should not be HTML escaped: 
{{{forbidden}}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html))
+        == "These characters should not be HTML escaped: & \" < >");
+
+    // Ampersand
+    CHECK(to_string("These characters should not be HTML escaped: 
{{&forbidden}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html))
+        == "These characters should not be HTML escaped: & \" < >");
+
+    // Basic Integer Interpolation
+    CHECK(to_string(R"("{{mph}} miles an hour!")"_fmt(object{{"mph", 85}})) == 
R"("85 miles an hour!")");
+
+    // Triple Mustache Integer Interpolation
+    CHECK(to_string(R"("{{{mph}}} miles an hour!")"_fmt(object{{"mph", 85}})) 
== R"("85 miles an hour!")");
+
+    // Ampersand Integer Interpolation
+    CHECK(to_string(R"("{{&mph}} miles an hour!")"_fmt(object{{"mph", 85}})) 
== R"("85 miles an hour!")");
+
+    // Basic Decimal Interpolation
+    CHECK(to_string(R"("{{power}} jiggawatts!")"_fmt(object{{"power", 1.21}})) 
== R"("1.21 jiggawatts!")");
+
+    // Triple Decimal Interpolation
+    CHECK(to_string(R"("{{{power}}} jiggawatts!")"_fmt(object{{"power", 
1.21}})) == R"("1.21 jiggawatts!")");
+
+    // Ampersand Decimal Interpolation
+    CHECK(to_string(R"("{{&power}} jiggawatts!")"_fmt(object{{"power", 
1.21}})) == R"("1.21 jiggawatts!")");
+
+    // Context Misses
+    {
+        // Basic Context Miss Interpolation
+        CHECK(to_string("I ({{cannot}}) be seen!"_fmt(empty)) == "I () be 
seen!");
+
+        // Triple Mustache Context Miss Interpolation
+        CHECK(to_string("I ({{{cannot}}}) be seen!"_fmt(empty)) == "I () be 
seen!");
+
+        // Ampersand Context Miss Interpolation
+        CHECK(to_string("I ({{&cannot}}) be seen!"_fmt(empty)) == "I () be 
seen!");
+    }
+
+    // Dotted Names
+    {
+        // Dotted Names - Basic Interpolation
+        CHECK(to_string(R"("{{person.name}}" == 
"{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", 
"Joe"}}}})) == R"("Joe" == "Joe")");
+
+        // Dotted Names - Triple Mustache Interpolation
+        CHECK(to_string(R"("{{{person.name}}}" == 
"{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", 
"Joe"}}}})) == R"("Joe" == "Joe")");
+
+        // Dotted Names - Ampersand Interpolation
+        CHECK(to_string(R"("{{&person.name}}" == 
"{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", 
"Joe"}}}})) == R"("Joe" == "Joe")");
+
+        // Dotted Names - Arbitrary Depth
+        CHECK(to_string(R"("{{a.b.c.d.e.name}}" == "Phil")"_fmt(
+            object{{"a", object{{"b", object{{"c", object{{"d", object{{"e", 
object{{"name", "Phil"}}}}}}}}}}}}))
+            == R"("Phil" == "Phil")");
+
+        // Dotted Names - Broken Chains
+        CHECK(to_string(R"("{{a.b.c}}" == "")"_fmt(empty)) == R"("" == "")");
+
+        // Dotted Names - Broken Chain Resolution
+        CHECK(to_string(R"("{{a.b.c.name}}" == "")"_fmt(
+            object{
+                {"a", object{{"b", empty}}},
+                {"c", object{{"name", "Jim"}}}
+            })) == R"("" == "")");
+
+        // Dotted Names - Initial Resolution
+        CHECK(to_string(R"("{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil")"_fmt(
+            object{
+                {"a", object{{"b", object{{"c", object{{"d", object{{"e", 
object{{"name", "Phil"}}}}}}}}}}},
+                {"c", object{{"c", object{{"d", object{{"e", object{{"name", 
"Wrong"}}}}}}}}}
+        })) == R"("Phil" == "Phil")");
+
+        // Dotted Names - Context Precedence
+        CHECK(to_string("{{#a}}{{b.c}}{{/a}}"_fmt(object{{"b", empty}, {"c", 
"ERROR"}})) == "");
+    }
+
+    object s{{"string", "---"}};
+
+    // Whitespace Sensitivity
+    {
+        // Interpolation - Surrounding Whitespace
+        CHECK(to_string("| {{string}} |"_fmt(s)) == "| --- |");
+
+        // Triple Mustache - Surrounding Whitespace
+        CHECK(to_string("| {{{string}}} |"_fmt(s)) == "| --- |");
+
+        // Ampersand - Surrounding Whitespace
+        CHECK(to_string("| {{&string}} |"_fmt(s)) == "| --- |");
+
+        // Interpolation - Standalone
+        CHECK(to_string("  {{string}}\n"_fmt(s)) == "  ---\n");
+
+        // Triple Mustache - Standalone
+        CHECK(to_string("  {{{string}}}\n"_fmt(s)) == "  ---\n");
+
+        // Ampersand - Standalone
+        CHECK(to_string("  {{&string}}\n"_fmt(s)) == "  ---\n");
+    }
+
+    // Whitespace Insensitivity
+    {
+        // Interpolation With Padding
+        CHECK(to_string("|{{ string }}|"_fmt(s)) == "|---|");
+
+        // Triple Mustache With Padding
+        CHECK(to_string("|{{{ string }}}|"_fmt(s)) == "|---|");
+
+        // Ampersand With Padding
+        CHECK(to_string("|{{& string }}|"_fmt(s)) == "|---|");
+    }
+}
+
+TEST_CASE("sections")
+{
+    object const empty;
+
+    // Truthy
+    CHECK(to_string(R"("{{#boolean}}This should be 
rendered.{{/boolean}}")"_fmt(object{{"boolean", true}}))
+        == R"("This should be rendered.")");
+
+    // Falsey
+    CHECK(to_string(R"("{{#boolean}}This should not be 
rendered.{{/boolean}}")"_fmt(object{{"boolean", false}}))
+        == R"("")");
+
+    // Context
+    CHECK(to_string(R"("{{#context}}Hi 
{{name}}.{{/context}}")"_fmt(object{{"context", object{{"name", "Joe"}}}}))
+        == R"("Hi Joe.")");
+
+    // Deeply Nested Contexts
+    CHECK(to_string(
+        "{{#a}}\n"
+        "{{one}}\n"
+        "{{#b}}\n"
+        "{{one}}{{two}}{{one}}\n"
+        "{{#c}}\n"
+        "{{one}}{{two}}{{three}}{{two}}{{one}}\n"
+        "{{#d}}\n"
+        "{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n"
+        "{{#e}}\n"
+        
"{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}\n"
+        "{{/e}}\n"
+        "{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n"
+        "{{/d}}\n"
+        "{{one}}{{two}}{{three}}{{two}}{{one}}\n"
+        "{{/c}}\n"
+        "{{one}}{{two}}{{one}}\n"
+        "{{/b}}\n"
+        "{{one}}\n"
+        "{{/a}}"_fmt(object{
+            {"a", object{{"one", 1}}},
+            {"b", object{{"two", 2}}},
+            {"c", object{{"three", 3}}},
+            {"d", object{{"four", 4}}},
+            {"e", object{{"five", 5}}}}))
+        == 
+        "1\n"
+        "121\n"
+        "12321\n"
+        "1234321\n"
+        "123454321\n"
+        "1234321\n"
+        "12321\n"
+        "121\n"
+        "1\n");
+
+    // List
+    CHECK(to_string(R"("{{#list}}{{item}}{{/list}}")"_fmt(object{{"list", 
array{object{{"item", 1}}, object{{"item", 2}}, object{{"item", 3}}}}}))
+        == R"("123")");
+
+    // Empty List
+    CHECK(to_string(R"("{{#list}}Yay lists!{{/list}}")"_fmt(object{{"list", 
array{}}})) == R"("")");
+
+    // Doubled
+    CHECK(to_string(
+        "{{#bool}}\n"
+        "* first\n"
+        "{{/bool}}\n"
+        "* {{two}}\n"
+        "{{#bool}}\n"
+        "* third\n"
+        "{{/bool}}"_fmt(object{{"bool", true}, {"two", "second"}}))
+        == 
+        "* first\n"
+        "* second\n"
+        "* third\n");
+
+    // Nested (Truthy)
+    CHECK(to_string("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E 
|"_fmt(object{{"bool", true}})) == "| A B C D E |");
+
+    // Nested (Falsey)
+    CHECK(to_string("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E 
|"_fmt(object{{"bool", false}})) == "| A  E |");
+
+    // Context Misses
+    CHECK(to_string("[{{#missing}}Found key 
'missing'!{{/missing}}]"_fmt(empty)) == "[]");
+
+    // Implicit Iterators
+    {
+        // Implicit Iterator - String
+        CHECK(to_string(R"#("{{#list}}({{.}}){{/list}}")#"_fmt(object{{"list", 
array{1, 2, 3, 4, 5}}})) == R"#("(1)(2)(3)(4)(5)")#");
+
+        // Implicit Iterator - Decimal
+        CHECK(to_string(R"#("{{#list}}({{.}}){{/list}}")#"_fmt(object{{"list", 
array{1.1, 2.2, 3.3, 4.4, 5.5}}})) == R"#("(1.1)(2.2)(3.3)(4.4)(5.5)")#");
+
+        // Implicit Iterator - Array
+        
CHECK(to_string(R"#("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}")#"_fmt(object{{"list",
 array{array{1, 2, 3}, array{"a", "b", "c"}}}})) == R"#("(123)(abc)")#");
+    }
+
+    // Dotted Names
+    {
+        // Dotted Names - Truthy
+        CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == 
"Here")"_fmt(object{{"a", object{{"b", object{{"c", true}}}}}})) == R"("Here" 
== "Here")");
+
+        // Dotted Names - Falsey
+        CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == "")"_fmt(object{{"a", 
object{{"b", object{{"c", false}}}}}})) == R"("" == "")");
+
+        // Dotted Names - Broken Chains
+        CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == "")"_fmt(object{{"a", 
empty}})) == R"("" == "")");
+    }
+
+    object const o{{"boolean", true}};
+
+    // Whitespace Sensitivity
+    {
+        // Surrounding Whitespace
+        CHECK(to_string(" | {{#boolean}}\t|\t{{/boolean}} | \n"_fmt(o)) == " | 
\t|\t | \n");
+
+        // Internal Whitespace
+        CHECK(to_string(" | {{#boolean}} {{! Important Whitespace }}\n 
{{/boolean}} | \n"_fmt(o)) == " |  \n  | \n");
+
+        // Indented Inline Sections
+        CHECK(to_string(" {{#boolean}}YES{{/boolean}}\n 
{{#boolean}}GOOD{{/boolean}}\n"_fmt(o)) == " YES\n GOOD\n");
+
+        // Standalone Lines
+        CHECK(to_string(
+            "| This Is\n"
+            "{{#boolean}}\n"
+            "|\n"
+            "{{/boolean}}\n"
+            "| A Line"_fmt(o))
+            ==
+            "| This Is\n"
+            "|\n"
+            "| A Line");
+
+        // Indented Standalone Lines
+        CHECK(to_string(
+            "| This Is\n"
+            "  {{#boolean}}\n"
+            "|\n"
+            "  {{/boolean}}\n"
+            "| A Line"_fmt(o))
+            ==
+            "| This Is\n"
+            "|\n"
+            "| A Line");
+
+        //  Standalone Line Endings
+        CHECK(to_string("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|"_fmt(o)) == 
"|\r\n|");
+
+        // Standalone Without Previous Line
+        CHECK(to_string("  {{#boolean}}\n#{{/boolean}}\n/"_fmt(o)) == "#\n/");
+
+        // Standalone Without Newline
+        CHECK(to_string("#{{#boolean}}\n/\n  {{/boolean}}"_fmt(o)) == 
"#\n/\n");
+    }
+
+    // Whitespace Insensitivity
+    {
+        CHECK(to_string("|{{# boolean }}={{/ boolean }}|"_fmt(o)) == "|=|");
+    }
+}
+
+TEST_CASE("inverted")
+{
+    object const empty;
+
+    // Falsey
+    CHECK(to_string(R"("{{^boolean}}This should be 
rendered.{{/boolean}}")"_fmt(object{{"boolean", false}})) == R"("This should be 
rendered.")");
+
+    // Truthy
+    CHECK(to_string(R"("{{^boolean}}This should not be 
rendered.{{/boolean}}")"_fmt(object{{"boolean", true}})) == R"("")");
+
+    // Context
+    CHECK(to_string(R"("{{^context}}Hi 
{{name}}.{{/context}}")"_fmt(object{{"context", object{{"name", "Joe"}}}})) == 
R"("")");
+
+    // List
+    CHECK(to_string(R"("{{^list}}{{n}}{{/list}}")"_fmt(object{{"list", 
array{object{{"n", 1}}, object{{"n", 2}}, object{{"n", 3}}}}})) == R"("")");
+
+    // Empty List
+    CHECK(to_string(R"("{{^list}}Yay lists!{{/list}}")"_fmt(object{{"list", 
array{}}})) == R"("Yay lists!")");
+
+    // Doubled
+    CHECK(to_string(
+        "{{^bool}}\n"
+        "* first\n"
+        "{{/bool}}\n"
+        "* {{two}}\n"
+        "{{^bool}}\n"
+        "* third\n"
+        "{{/bool}}"_fmt(object{{"bool", false}, {"two", "second"}}))
+        ==
+        "* first\n"
+        "* second\n"
+        "* third\n");
+
+    // Nested (Falsey)
+    CHECK(to_string("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E 
|"_fmt(object{{"bool", false}})) == "| A B C D E |");
+
+    // Nested (Truthy)
+    CHECK(to_string("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E 
|"_fmt(object{{"bool", true}})) == "| A  E |");
+
+    // Context Misses
+    CHECK(to_string("[{{^missing}}Cannot find key 
'missing'!{{/missing}}]"_fmt(empty)) == "[Cannot find key 'missing'!]");
+
+    // Dotted Names
+    {
+        // Dotted Names - Truthy
+        CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == 
"")"_fmt(object{{"a", object{{"b", object{{"c", true}}}}}})) == R"("" == "")");
+
+        // Dotted Names - Falsey
+        CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == "Not 
Here")"_fmt(object{{"a", object{{"b", object{{"c", false}}}}}})) == R"("Not 
Here" == "Not Here")");
+
+        // Dotted Names - Broken Chains
+        CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == "Not 
Here")"_fmt(object{{"a", empty}})) == R"("Not Here" == "Not Here")");
+    }
+
+    object const o{{"boolean", false}};
+
+    // Whitespace Sensitivity
+    {
+        // Surrounding Whitespace
+        CHECK(to_string(" | {{^boolean}}\t|\t{{/boolean}} | \n"_fmt(o)) == " | 
\t|\t | \n");
+
+        // Internal Whitespace
+        CHECK(to_string(" | {{^boolean}} {{! Important Whitespace }}\n 
{{/boolean}} | \n"_fmt(o)) == " |  \n  | \n");
+
+        // Indented Inline Sections
+        CHECK(to_string(" {{^boolean}}YES{{/boolean}}\n 
{{^boolean}}GOOD{{/boolean}}\n"_fmt(o)) == " YES\n GOOD\n");
+
+        // Standalone Lines
+        CHECK(to_string(
+            "| This Is\n"
+            "{{^boolean}}\n"
+            "|\n"
+            "{{/boolean}}\n"
+            "| A Line"_fmt(o))
+            ==
+            "| This Is\n"
+            "|\n"
+            "| A Line");
+
+        // Indented Standalone Lines
+        CHECK(to_string(
+            "| This Is\n"
+            "  {{^boolean}}\n"
+            "|\n"
+            "  {{/boolean}}\n"
+            "| A Line"_fmt(o))
+            ==
+            "| This Is\n"
+            "|\n"
+            "| A Line");
+
+        //  Standalone Line Endings
+        CHECK(to_string("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|"_fmt(o)) == 
"|\r\n|");
+
+        // Standalone Without Previous Line
+        CHECK(to_string("  {{^boolean}}\n#{{/boolean}}\n/"_fmt(o)) == "#\n/");
+
+        // Standalone Without Newline
+        CHECK(to_string("#{{^boolean}}\n/\n  {{/boolean}}"_fmt(o)) == 
"#\n/\n");
+    }
+
+    // Whitespace Insensitivity
+    {
+        CHECK(to_string("|{{^ boolean }}={{/ boolean }}|"_fmt(o)) == "|=|");
+    }
+}
+
+TEST_CASE("delimiters")
+{
+    // Pair Behavior
+    CHECK(to_string("{{=<% %>=}}(<%text%>)"_fmt(object{{"text", "Hey!"}})) == 
"(Hey!)");
+
+    // Special Characters
+    CHECK(to_string("({{=[ ]=}}[text])"_fmt(object{{"text", "It worked!"}})) 
== "(It worked!)");
+
+    // Sections
+    CHECK(to_string(
+        "[\n"
+        "{{#section}}\n"
+        "  {{data}}\n"
+        "  |data|\n"
+        "{{/section}}\n"
+        "{{= | | =}}\n"
+        "|#section|\n"
+        "  {{data}}\n"
+        "  |data|\n"
+        "|/section|\n"
+        "]"_fmt(object{{"section", true}, {"data", "I got interpolated."}}))
+        == 
+        "[\n"
+        "  I got interpolated.\n"
+        "  |data|\n"
+        "  {{data}}\n"
+        "  I got interpolated.\n"
+        "]");
+
+    // Inverted Sections
+    CHECK(to_string(
+        "[\n"
+        "{{^section}}\n"
+        "  {{data}}\n"
+        "  |data|\n"
+        "{{/section}}\n"
+        "{{= | | =}}\n"
+        "|^section|\n"
+        "  {{data}}\n"
+        "  |data|\n"
+        "|/section|\n"
+        "]"_fmt(object{{"section", false},{"data", "I got interpolated."}}))
+        ==
+        "[\n"
+        "  I got interpolated.\n"
+        "  |data|\n"
+        "  {{data}}\n"
+        "  I got interpolated.\n"
+        "]");
+
+    // Partial Inheritence
+    CHECK(to_string(
+        "[ {{>include}} ]\n"
+        "{{= | | =}}\n"
+        "[ |>include| ]"_fmt(object{{"value", "yes"}}, context{{"include", 
".{{value}}."_fmt}}))
+        ==
+        "[ .yes. ]\n"
+        "[ .yes. ]");
+
+    // Post-Partial Behavior
+    CHECK(to_string(
+        "[ {{>include}} ]\n"
+        "[ .{{value}}.  .|value|. ]"_fmt(object{{"value", "yes"}}, 
context{{"include", ".{{value}}. {{= | | =}} .|value|."_fmt}}))
+        ==
+        "[ .yes.  .yes. ]\n"
+        "[ .yes.  .|value|. ]");
+
+    object const empty;
+
+    // Whitespace Sensitivity
+    {
+        // Surrounding Whitespace
+        CHECK(to_string("| {{=@ @=}} |"_fmt(empty)) == "|  |");
+
+        // Outlying Whitespace (Inline)
+        CHECK(to_string(" | {{=@ @=}}\n"_fmt(empty)) == " | \n");
+
+        // Standalone Tag
+        CHECK(to_string(
+            "Begin.\n"
+            "{{=@ @=}}\n"
+            "End."_fmt(empty))
+            == 
+            "Begin.\n"
+            "End.");
+
+        // Indented Standalone Tag
+        CHECK(to_string(
+            "Begin.\n"
+            "  {{=@ @=}}\n"
+            "End."_fmt(empty))
+            ==
+            "Begin.\n"
+            "End.");
+
+        // Standalone Line Endings
+        CHECK(to_string("|\r\n{{= @ @ =}}\r\n|"_fmt(empty)) == "|\r\n|");
+
+        // Standalone Without Previous Line
+        CHECK(to_string("  {{=@ @=}}\n="_fmt(empty)) == "=");
+
+        // Standalone Without Newline
+        CHECK(to_string("=\n  {{=@ @=}}"_fmt(empty)) == "=\n");
+    }
+
+    // Whitespace Insensitivity
+    {
+        // Pair with Padding
+        CHECK(to_string("|{{= @   @ =}}|"_fmt(empty)) == "||");
+    }
+}
+
+TEST_CASE("comments")
+{
+    object const empty;
+
+    // Inline
+    CHECK(to_string("12345{{! Comment Block! }}67890"_fmt(empty)) == 
"1234567890");
+
+    // Multiline
+    CHECK(to_string(
+        "12345{{!\n"
+        "  This is a\n"
+        "  multi-line comment...\n"
+        "}}67890"_fmt(empty))
+        == 
+        "1234567890");
+
+    // Standalone
+    CHECK(to_string(
+        "Begin.\n"
+        "{{! Comment Block! }}\n"
+        "End."_fmt(empty))
+        ==
+        "Begin.\n"
+        "End.");
+
+    // Indented Standalone
+    CHECK(to_string(
+        "Begin.\n"
+        "  {{! Comment Block! }}\n"
+        "End."_fmt(empty))
+        ==
+        "Begin.\n"
+        "End.");
+
+    // Standalone Line Endings
+    CHECK(to_string("|\r\n{{! Standalone Comment }}\r\n|"_fmt(empty)) == 
"|\r\n|");
+
+    // Standalone Without Previous Line
+    CHECK(to_string("  {{! I'm Still Standalone }}\n!"_fmt(empty)) == "!");
+
+    // Standalone Without Newline
+    CHECK(to_string("!\n  {{! I'm Still Standalone }}"_fmt(empty)) == "!\n");
+
+    // Multiline Standalone
+    CHECK(to_string(
+        "Begin.\n"
+        "{{!\n"
+        "Something's going on here...\n"
+        "}}\n"
+        "End."_fmt(empty))
+        ==
+        "Begin.\n"
+        "End.");
+
+    // Indented Multiline Standalone
+    CHECK(to_string(
+        "Begin.\n"
+        "  {{!\n"
+        "    Something's going on here...\n"
+        "  }}\n"
+        "End."_fmt(empty))
+        ==
+        "Begin.\n"
+        "End.");
+
+    // Indented Inline
+    CHECK(to_string("  12 {{! 34 }}\n"_fmt(empty)) == "  12 \n");
+
+    // Surrounding Whitespace
+    CHECK(to_string("12345 {{! Comment Block! }} 67890"_fmt(empty)) == "12345  
67890");
+}
+
+TEST_CASE("partials")
+{
+    object const empty;
+
+    // Basic Behavior
+    CHECK(to_string(R"("{{>text}}")"_fmt(empty, context{{"text", "from 
partial"_fmt}})) == R"("from partial")");
+
+    // Failed Lookup
+    CHECK(to_string(R"("{{>text}}")"_fmt(empty)) == R"("")");
+
+    // Context
+    CHECK(to_string(R"("{{>partial}}")"_fmt(object{{"text", "content"}}, 
context{{"partial", "*{{text}}*"_fmt}})) == R"("*content*")");
+
+    // Recursion
+    CHECK(to_string("{{>node}}"_fmt(object{
+        {"content", "X"},
+        {"nodes", array{object{{"content", "Y"}, {"nodes", array{}}}}}
+    }, context{{"node", "{{content}}<{{#nodes}}{{>node}}{{/nodes}}>"_fmt}})) 
== "X<Y<>>");
+
+    // Whitespace Sensitivity
+    {
+        // Surrounding Whitespace
+        CHECK(to_string("| {{>partial}} |"_fmt(empty, context{{"partial", 
"\t|\t"_fmt}})) == "| \t|\t |");
+
+        // Inline Indentation
+        CHECK(to_string("  {{data}}  {{> partial}}\n"_fmt(object{{"data", 
"|"}}, context{{"partial", ">\n>"_fmt}})) == "  |  >\n>\n");
+
+        // Standalone Line Endings
+        CHECK(to_string("|\r\n{{>partial}}\r\n|"_fmt(empty, 
context{{"partial", ">"_fmt}})) == "|\r\n>|");
+
+        // Standalone Without Previous Line
+        CHECK(to_string("  {{>partial}}\n>"_fmt(empty, context{{"partial", 
">\n>"_fmt}})) == "  >\n  >>");
+
+        // Standalone Without Newline
+        CHECK(to_string(">\n  {{>partial}}"_fmt(empty, context{{"partial", 
">\n>"_fmt}})) == ">\n  >\n  >");
+
+        // Standalone Indentation
+        CHECK(to_string(
+            "\\\n"
+            " {{>partial}}\n"
+            "/"_fmt(object{{"content", "<\n->"}},
+                context{{"partial",
+                "|\n"
+                "{{{content}}}\n"
+                "|\n"_fmt}}))
+            == 
+            "\\\n"
+            " |\n"
+            " <\n"
+            "->\n"
+            " |\n"
+            "/");
+    }
+
+    // Whitespace Insensitivity
+    {
+        // Padding Whitespace
+        CHECK(to_string("|{{> partial }}|"_fmt(empty, context{{"partial", 
"[]"_fmt}})) == "|[]|");
+    }
+}
+
+TEST_CASE("lambdas")
+{
+    // Interpolation
+    CHECK(to_string("Hello, {{lambda}}!"_fmt(object{{"lambda", [] { return 
"world"; }}})) == "Hello, world!");
+
+    // Interpolation - Expansion
+    CHECK(to_string(
+        "Hello, {{lambda}}!"_fmt(object{
+            {"lambda", [] { return "{{planet}}"_fmt; }},
+            {"planet", "world"}}))
+        ==
+        "Hello, world!");
+
+    // Interpolation - Alternate Delimiters
+    CHECK(to_string(
+        "{{= | | =}}\nHello, (|&lambda|)!"_fmt(object{
+            {"lambda", [] { return "|planet| => {{planet}}"_fmt; }},
+            {"planet", "world"}}))
+        ==
+        "Hello, (|planet| => world)!");
+
+    // Interpolation - Multiple Calls
+    CHECK(to_string(
+        "{{lambda}} == {{{lambda}}} == {{lambda}}"_fmt(object{
+            {"lambda", [n = 0]() mutable { return ++n; }}}))
+        ==
+        "1 == 2 == 3");
+
+    // Escaping
+    CHECK(to_string("<{{lambda}}{{{lambda}}}"_fmt(object{{"lambda", [] { 
return ">"; }}}, escape_html)) == "<&gt;>");
+
+    // Section - Expansion
+    CHECK(to_string("<{{#lambda}}-{{/lambda}}>"_fmt(object{
+        {"lambda", [](ast::content_list const& contents) {
+            ast::content_list list;
+            list.insert(list.end(), contents.begin(), contents.end());
+            list.push_back(ast::variable{"planet"});
+            list.insert(list.end(), contents.begin(), contents.end());
+            return format(std::move(list), false);
+        }},
+        {"planet", "Earth"}}))
+        ==
+        "<-Earth->");
+
+    // Section - Multiple Calls
+    CHECK(to_string("{{#lambda}}FILE{{/lambda}} != 
{{#lambda}}LINE{{/lambda}}"_fmt(object{
+        {"lambda", [](ast::content_list const& contents) {
+            ast::content_list list;
+            list.push_back(ast::text("__"));
+            list.insert(list.end(), contents.begin(), contents.end());
+            list.push_back(ast::text("__"));
+            return format(std::move(list), false);
+        }}}))
+        ==
+        "__FILE__ != __LINE__");
+
+    // Inverted Section
+    CHECK(to_string("<{{^lambda}}{{static}}{{/lambda}}>"_fmt(object{
+        {"lambda", [](ast::content_list const&) { return false; }},
+        {"static", "static"}}))
+        ==
+        "<>");
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/variant.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/bustache/test/variant.cpp 
b/thirdparty/bustache/test/variant.cpp
new file mode 100644
index 0000000..9e04858
--- /dev/null
+++ b/thirdparty/bustache/test/variant.cpp
@@ -0,0 +1,233 @@
+/*//////////////////////////////////////////////////////////////////////////////
+    Copyright (c) 2016-2017 Jamboree
+
+    Distributed under the Boost Software License, Version 1.0. (See 
accompanying
+    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//////////////////////////////////////////////////////////////////////////////*/
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
+#include <bustache/detail/variant.hpp>
+
+struct BadCopyError {};
+
+struct BadCopy
+{
+    BadCopy() = default;
+
+    BadCopy(BadCopy&&) = default;
+
+    BadCopy(BadCopy const&)
+    {
+        throw BadCopyError();
+    }
+
+    BadCopy& operator=(BadCopy const&)
+    {
+        throw BadCopyError();
+    }
+
+    BadCopy& operator=(BadCopy&&) = default;
+};
+
+struct A
+{
+    A() = default;
+
+    A(A&& other) noexcept
+    {
+        other.moved = true;
+    }
+
+    A(A const&) = default;
+
+    A& operator=(A&& other) noexcept
+    {
+        assigned = true;
+        other.moved = true;
+        return *this;
+    }
+
+    A& operator=(A const& other) noexcept
+    {
+        assigned = true;
+        return *this;
+    }
+
+    bool assigned = false;
+    bool moved = false;
+};
+
+struct GoodInt
+{
+    operator int() const noexcept
+    {
+        return 0;
+    }
+};
+
+struct BadIntError {};
+
+struct BadInt
+{
+    operator int() const
+    {
+        throw BadIntError();
+    }
+};
+
+struct Visitor
+{
+    unsigned operator()(bool const&) const
+    {
+        return 0;
+    }
+
+    unsigned operator()(int const&) const
+    {
+        return 1;
+    }
+
+    unsigned operator()(A const&) const
+    {
+        return 2;
+    }
+
+    unsigned operator()(BadCopy const&) const
+    {
+        return 3;
+    }
+};
+
+namespace bustache
+{
+#define VAR(X, D)                                                              
 \
+    X(0, bool, D)                                                              
 \
+    X(1, int, D)                                                               
 \
+    X(2, A, D)                                                                 
 \
+    X(3, BadCopy, D)                                                           
 \
+/***/
+    class Var : public variant_base<Var>
+    {
+        VAR(Zz_BUSTACHE_VARIANT_MATCH,)
+    public:
+        Zz_BUSTACHE_VARIANT_DECL(Var, VAR, false)
+
+        Var() noexcept : _which(0), _0() {}
+    };
+#undef VAR
+}
+
+using namespace bustache;
+
+TEST_CASE("variant-ctor")
+{
+    {
+        Var v;
+        CHECK(v.valid());
+        CHECK(v.which() == 0);
+        Var v2(v);
+        CHECK(v.which() == 0);
+    }
+    {
+        Var v(true);
+        CHECK(v.valid());
+        CHECK(v.which() == 0);
+        Var v2(v);
+        CHECK(v.which() == 0);
+    }
+    {
+        Var v(0);
+        CHECK(v.valid());
+        CHECK(v.which() == 1);
+        Var v2(v);
+        CHECK(v.which() == 1);
+    }
+    {
+        Var v(A{});
+        CHECK(v.valid());
+        CHECK(v.which() == 2);
+        Var v2(v);
+        CHECK(v.which() == 2);
+    }
+    {
+        Var v(BadCopy{});
+        CHECK(v.valid());
+        CHECK(v.which() == 3);
+        CHECK_THROWS_AS(Var{v}, BadCopyError);
+    }
+    {   // Test convertible.
+        Var v(GoodInt{});
+        CHECK(v.valid());
+        CHECK(v.which() == 1);
+    }
+    {
+        Var v1(A{});
+        CHECK(v1.which() == 2);
+        Var v2(std::move(v1));
+        CHECK(v1.which() == 2);
+        CHECK(v2.which() == 2);
+        CHECK(get<A>(v1).moved == true);
+    }
+}
+
+TEST_CASE("variant-access")
+{
+    Var v;
+    CHECK(v.which() == 0);
+    CHECK(get<bool>(&v) != nullptr);
+    CHECK(get<bool>(v) == false);
+    CHECK(get<int>(&v) == nullptr);
+    CHECK_THROWS_AS(get<int>(v), bad_variant_access);
+    v = 1024;
+    CHECK(v.which() == 1);
+    CHECK(get<int>(&v) != nullptr);
+    CHECK(get<int>(v) == 1024);
+    get<int>(v) = true;
+    CHECK(v.which() == 1);
+    CHECK(get<int>(v) == 1);
+    v = true;
+    CHECK(v.which() == 0);
+    CHECK(get<bool>(v) == true);
+    CHECK_THROWS_AS(get<A>(v), bad_variant_access);
+    {
+        REQUIRE(v.which() != 2);
+        auto& a = v = A();
+        CHECK(v.which() == 2);
+        CHECK(get<A>(&v) != nullptr);
+        CHECK(get<A>(&v) == &a);
+        CHECK(!a.assigned);
+    }
+    {
+        REQUIRE(v.which() == 2);
+        auto& b = v = A();
+        CHECK(v.which() == 2);
+        CHECK(get<A>(&v) == &b);
+        CHECK(b.assigned);
+    }
+}
+
+TEST_CASE("variant-valuess-by-exception")
+{
+    Var v;
+    CHECK(v.valid());
+    CHECK_THROWS_AS(v = BadInt(), BadIntError);
+    CHECK(v.which() != 0);
+    CHECK(!v.valid());
+    v = 42;
+    CHECK(v.valid());
+
+    Var v2(BadCopy{});
+    CHECK_THROWS_AS(v = v2, BadCopyError);
+    CHECK(!v.valid());
+    CHECK(v2.which() == 3);
+}
+
+TEST_CASE("variant-visit")
+{
+    Visitor v;
+    CHECK(visit(v, Var{}) == 0);
+    CHECK(visit(v, Var{true}) == 0);
+    CHECK(visit(v, Var{0}) == 1);
+    CHECK(visit(v, Var{A{}}) == 2);
+    CHECK(visit(v, Var{BadCopy{}}) == 3);
+}
\ No newline at end of file

Reply via email to