aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2022-01-20 14:53:53 +0100
committerJay Berkenbilt <ejb@ql.org>2022-01-30 19:11:03 +0100
commit1db0a7ffcee6f6ae6bd3298a960665378d304fa1 (patch)
tree12c0f9df760943dd323b5b0286c1da7aaaaf0728
parentacf8d18b6e41ec9786bd059795731e92998620ab (diff)
downloadqpdf-1db0a7ffcee6f6ae6bd3298a960665378d304fa1.tar.zst
JSONHandler: rework dictionary and array handlers
-rw-r--r--include/qpdf/JSONHandler.hh37
-rw-r--r--libqpdf/JSONHandler.cc39
-rw-r--r--libtests/json_handler.cc44
-rw-r--r--libtests/qtest/json_handler/json_handler.out9
4 files changed, 89 insertions, 40 deletions
diff --git a/include/qpdf/JSONHandler.hh b/include/qpdf/JSONHandler.hh
index bde134ac..0f75f946 100644
--- a/include/qpdf/JSONHandler.hh
+++ b/include/qpdf/JSONHandler.hh
@@ -31,6 +31,11 @@
#include <stdexcept>
#include <memory>
+// This class allows a sax-like walk through a JSON object with
+// functionality that mostly mirrors QPDFArgParser. It is primarily
+// here to facilitate automatic generation of some of the code to help
+// keep QPDFJob json consistent with command-line arguments.
+
class JSONHandler
{
public:
@@ -53,7 +58,8 @@ class JSONHandler
// certain type. JSONHandler::Error is thrown otherwise. Multiple
// handlers may be registered, which allows the object to be of
// various types. If an anyHandler is added, no other handler will
- // be called.
+ // be called. There is no "final" handler -- if the top-level is a
+ // dictionary or array, just use its end handler.
typedef std::function<void(
std::string const& path, JSON value)> json_handler_t;
@@ -80,19 +86,18 @@ class JSONHandler
QPDF_DLL
void addBoolHandler(bool_handler_t fn);
- // Returns a reference to a map: keys are expected object keys,
- // and values are handlers for that object.
QPDF_DLL
- std::map<std::string, std::shared_ptr<JSONHandler>>& addDictHandlers();
-
- // Apply the given handler to any key not explicitly in dict
- // handlers.
+ void addDictHandlers(void_handler_t start_fn, void_handler_t end_fn);
+ QPDF_DLL
+ void addDictKeyHandler(
+ std::string const& key, std::shared_ptr<JSONHandler>);
QPDF_DLL
void addFallbackDictHandler(std::shared_ptr<JSONHandler>);
- // Apply the given handler to each element of the array.
QPDF_DLL
- void addArrayHandler(std::shared_ptr<JSONHandler>);
+ void addArrayHandlers(void_handler_t start_fn,
+ void_handler_t end_fn,
+ std::shared_ptr<JSONHandler> item_handlers);
// Apply handlers recursively to a JSON object.
QPDF_DLL
@@ -108,7 +113,12 @@ class JSONHandler
null_handler(nullptr),
string_handler(nullptr),
number_handler(nullptr),
- bool_handler(nullptr)
+ bool_handler(nullptr),
+ dict_start_handler(nullptr),
+ dict_end_handler(nullptr),
+ array_start_handler(nullptr),
+ array_end_handler(nullptr),
+ final_handler(nullptr)
{
}
@@ -117,9 +127,14 @@ class JSONHandler
string_handler_t string_handler;
string_handler_t number_handler;
bool_handler_t bool_handler;
+ void_handler_t dict_start_handler;
+ void_handler_t dict_end_handler;
+ void_handler_t array_start_handler;
+ void_handler_t array_end_handler;
+ void_handler_t final_handler;
std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers;
std::shared_ptr<JSONHandler> fallback_dict_handler;
- std::shared_ptr<JSONHandler> array_handler;
+ std::shared_ptr<JSONHandler> array_item_handler;
};
class Members
diff --git a/libqpdf/JSONHandler.cc b/libqpdf/JSONHandler.cc
index 7318466f..5e3d9a8d 100644
--- a/libqpdf/JSONHandler.cc
+++ b/libqpdf/JSONHandler.cc
@@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn)
this->m->h.bool_handler = fn;
}
-std::map<std::string, std::shared_ptr<JSONHandler>>&
-JSONHandler::addDictHandlers()
+void
+JSONHandler::addDictHandlers(void_handler_t start_fn, void_handler_t end_fn)
+{
+ this->m->h.dict_start_handler = start_fn;
+ this->m->h.dict_end_handler = end_fn;
+}
+
+void
+JSONHandler::addDictKeyHandler(
+ std::string const& key, std::shared_ptr<JSONHandler> dkh)
{
- return this->m->h.dict_handlers;
+ this->m->h.dict_handlers[key] = dkh;
}
void
@@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr<JSONHandler> fdh)
}
void
-JSONHandler::addArrayHandler(std::shared_ptr<JSONHandler> ah)
+JSONHandler::addArrayHandlers(void_handler_t start_fn,
+ void_handler_t end_fn,
+ std::shared_ptr<JSONHandler> ah)
{
- this->m->h.array_handler = ah;
+ this->m->h.array_start_handler = start_fn;
+ this->m->h.array_end_handler = end_fn;
+ this->m->h.array_item_handler = ah;
}
void
@@ -95,9 +107,9 @@ JSONHandler::handle(std::string const& path, JSON j)
this->m->h.bool_handler(path, bvalue);
handled = true;
}
- if ((this->m->h.fallback_dict_handler.get() ||
- (! this->m->h.dict_handlers.empty())) && j.isDictionary())
+ if (this->m->h.dict_start_handler && j.isDictionary())
{
+ this->m->h.dict_start_handler(path);
std::string path_base = path;
if (path_base != ".")
{
@@ -126,22 +138,19 @@ JSONHandler::handle(std::string const& path, JSON j)
i->second->handle(path_base + k, v);
}
});
-
- // Set handled = true even if we didn't call any handlers.
- // This dictionary could have been empty, but it's okay since
- // it's a dictionary like it's supposed to be.
+ this->m->h.dict_end_handler(path);
handled = true;
}
- if (this->m->h.array_handler.get())
+ if (this->m->h.array_start_handler && j.isArray())
{
+ this->m->h.array_start_handler(path);
size_t i = 0;
j.forEachArrayItem([&i, &path, this](JSON v) {
- this->m->h.array_handler->handle(
+ this->m->h.array_item_handler->handle(
path + "[" + QUtil::uint_to_string(i) + "]", v);
++i;
});
- // Set handled = true even if we didn't call any handlers.
- // This could have been an empty array.
+ this->m->h.array_end_handler(path);
handled = true;
}
diff --git a/libtests/json_handler.cc b/libtests/json_handler.cc
index d5e6aea3..7f6349f9 100644
--- a/libtests/json_handler.cc
+++ b/libtests/json_handler.cc
@@ -28,6 +28,13 @@ static void print_json(std::string const& path, JSON value)
std::cout << path << ": json: " << value.unparse() << std::endl;
}
+static JSONHandler::void_handler_t make_print_message(std::string msg)
+{
+ return [msg](std::string const& path) {
+ std::cout << path << ": json: " << msg << std::endl;
+ };
+}
+
static void test_scalar()
{
std::cout << "-- scalar --" << std::endl;
@@ -40,41 +47,50 @@ static void test_scalar()
static std::shared_ptr<JSONHandler> make_all_handler()
{
auto h = std::make_shared<JSONHandler>();
- auto& m = h->addDictHandlers();
+ h->addDictHandlers(
+ make_print_message("dict begin"),
+ make_print_message("dict end"));
auto h1 = std::make_shared<JSONHandler>();
h1->addStringHandler(print_string);
- m["one"] = h1;
+ h->addDictKeyHandler("one", h1);
auto h2 = std::make_shared<JSONHandler>();
h2->addNumberHandler(print_number);
- m["two"] = h2;
+ h->addDictKeyHandler("two", h2);
auto h3 = std::make_shared<JSONHandler>();
h3->addBoolHandler(print_bool);
- m["three"] = h3;
+ h->addDictKeyHandler("three", h3);
auto h4 = std::make_shared<JSONHandler>();
h4->addAnyHandler(print_json);
- m["four"] = h4;
- m["phour"] = h4; // share h4
+ h->addDictKeyHandler("four", h4);
+ h->addDictKeyHandler("phour", h4); // share h4
auto h5 = std::make_shared<JSONHandler>();
// Allow to be either string or bool
h5->addBoolHandler(print_bool);
h5->addStringHandler(print_string);
h5->addNullHandler(print_null);
auto h5s = std::make_shared<JSONHandler>();
- m["five"] = h5s;
- h5s->addArrayHandler(h5);
+ h->addDictKeyHandler("five", h5s);
+ h5s->addArrayHandlers(
+ make_print_message("array begin"),
+ make_print_message("array end"),
+ h5);
auto h6 = std::make_shared<JSONHandler>();
- auto& m6 = h6->addDictHandlers();
+ h6->addDictHandlers(
+ make_print_message("dict begin"),
+ make_print_message("dict end"));
auto h6a = std::make_shared<JSONHandler>();
- m6["a"] = h6a;
- auto& m6a = h6a->addDictHandlers();
+ h6->addDictKeyHandler("a", h6a);
+ h6a->addDictHandlers(
+ make_print_message("dict begin"),
+ make_print_message("dict end"));
auto h6ab = std::make_shared<JSONHandler>();
- m6a["b"] = h6ab;
+ h6a->addDictKeyHandler("b", h6ab);
auto h6ax = std::make_shared<JSONHandler>();
h6ax->addAnyHandler(print_json);
h6a->addFallbackDictHandler(h6ax);
- m6["b"] = h6ab; // share
+ h6->addDictKeyHandler("b", h6ab); // share
h6ab->addStringHandler(print_string);
- m["six"] = h6;
+ h->addDictKeyHandler("six", h6);
return h;
}
diff --git a/libtests/qtest/json_handler/json_handler.out b/libtests/qtest/json_handler/json_handler.out
index 13554af3..368c94b5 100644
--- a/libtests/qtest/json_handler/json_handler.out
+++ b/libtests/qtest/json_handler/json_handler.out
@@ -1,22 +1,31 @@
-- scalar --
.: string: potato
-- all --
+.: json: dict begin
+.five: json: array begin
.five[0]: string: x
.five[1]: bool: false
.five[2]: string: y
.five[3]: null
.five[4]: bool: true
+.five: json: array end
.four: json: [
"a",
1
]
.one: string: potato
.phour: json: null
+.six: json: dict begin
+.six.a: json: dict begin
.six.a.Q: json: "baaa"
.six.a.b: string: quack
+.six.a: json: dict end
.six.b: string: moo
+.six: json: dict end
.three: bool: true
.two: number: 3.14
+.: json: dict end
-- errors --
bad type at top: JSON handler: value at . is not of expected type
+.: json: dict begin
unexpected key: JSON handler found unexpected key x in object at .