Commit 1db0a7ffcee6f6ae6bd3298a960665378d304fa1

Authored by Jay Berkenbilt
1 parent acf8d18b

JSONHandler: rework dictionary and array handlers

include/qpdf/JSONHandler.hh
@@ -31,6 +31,11 @@ @@ -31,6 +31,11 @@
31 #include <stdexcept> 31 #include <stdexcept>
32 #include <memory> 32 #include <memory>
33 33
  34 +// This class allows a sax-like walk through a JSON object with
  35 +// functionality that mostly mirrors QPDFArgParser. It is primarily
  36 +// here to facilitate automatic generation of some of the code to help
  37 +// keep QPDFJob json consistent with command-line arguments.
  38 +
34 class JSONHandler 39 class JSONHandler
35 { 40 {
36 public: 41 public:
@@ -53,7 +58,8 @@ class JSONHandler @@ -53,7 +58,8 @@ class JSONHandler
53 // certain type. JSONHandler::Error is thrown otherwise. Multiple 58 // certain type. JSONHandler::Error is thrown otherwise. Multiple
54 // handlers may be registered, which allows the object to be of 59 // handlers may be registered, which allows the object to be of
55 // various types. If an anyHandler is added, no other handler will 60 // various types. If an anyHandler is added, no other handler will
56 - // be called. 61 + // be called. There is no "final" handler -- if the top-level is a
  62 + // dictionary or array, just use its end handler.
57 63
58 typedef std::function<void( 64 typedef std::function<void(
59 std::string const& path, JSON value)> json_handler_t; 65 std::string const& path, JSON value)> json_handler_t;
@@ -80,19 +86,18 @@ class JSONHandler @@ -80,19 +86,18 @@ class JSONHandler
80 QPDF_DLL 86 QPDF_DLL
81 void addBoolHandler(bool_handler_t fn); 87 void addBoolHandler(bool_handler_t fn);
82 88
83 - // Returns a reference to a map: keys are expected object keys,  
84 - // and values are handlers for that object.  
85 QPDF_DLL 89 QPDF_DLL
86 - std::map<std::string, std::shared_ptr<JSONHandler>>& addDictHandlers();  
87 -  
88 - // Apply the given handler to any key not explicitly in dict  
89 - // handlers. 90 + void addDictHandlers(void_handler_t start_fn, void_handler_t end_fn);
  91 + QPDF_DLL
  92 + void addDictKeyHandler(
  93 + std::string const& key, std::shared_ptr<JSONHandler>);
90 QPDF_DLL 94 QPDF_DLL
91 void addFallbackDictHandler(std::shared_ptr<JSONHandler>); 95 void addFallbackDictHandler(std::shared_ptr<JSONHandler>);
92 96
93 - // Apply the given handler to each element of the array.  
94 QPDF_DLL 97 QPDF_DLL
95 - void addArrayHandler(std::shared_ptr<JSONHandler>); 98 + void addArrayHandlers(void_handler_t start_fn,
  99 + void_handler_t end_fn,
  100 + std::shared_ptr<JSONHandler> item_handlers);
96 101
97 // Apply handlers recursively to a JSON object. 102 // Apply handlers recursively to a JSON object.
98 QPDF_DLL 103 QPDF_DLL
@@ -108,7 +113,12 @@ class JSONHandler @@ -108,7 +113,12 @@ class JSONHandler
108 null_handler(nullptr), 113 null_handler(nullptr),
109 string_handler(nullptr), 114 string_handler(nullptr),
110 number_handler(nullptr), 115 number_handler(nullptr),
111 - bool_handler(nullptr) 116 + bool_handler(nullptr),
  117 + dict_start_handler(nullptr),
  118 + dict_end_handler(nullptr),
  119 + array_start_handler(nullptr),
  120 + array_end_handler(nullptr),
  121 + final_handler(nullptr)
112 { 122 {
113 } 123 }
114 124
@@ -117,9 +127,14 @@ class JSONHandler @@ -117,9 +127,14 @@ class JSONHandler
117 string_handler_t string_handler; 127 string_handler_t string_handler;
118 string_handler_t number_handler; 128 string_handler_t number_handler;
119 bool_handler_t bool_handler; 129 bool_handler_t bool_handler;
  130 + void_handler_t dict_start_handler;
  131 + void_handler_t dict_end_handler;
  132 + void_handler_t array_start_handler;
  133 + void_handler_t array_end_handler;
  134 + void_handler_t final_handler;
120 std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; 135 std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers;
121 std::shared_ptr<JSONHandler> fallback_dict_handler; 136 std::shared_ptr<JSONHandler> fallback_dict_handler;
122 - std::shared_ptr<JSONHandler> array_handler; 137 + std::shared_ptr<JSONHandler> array_item_handler;
123 }; 138 };
124 139
125 class Members 140 class Members
libqpdf/JSONHandler.cc
@@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn) @@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn)
46 this->m->h.bool_handler = fn; 46 this->m->h.bool_handler = fn;
47 } 47 }
48 48
49 -std::map<std::string, std::shared_ptr<JSONHandler>>&  
50 -JSONHandler::addDictHandlers() 49 +void
  50 +JSONHandler::addDictHandlers(void_handler_t start_fn, void_handler_t end_fn)
  51 +{
  52 + this->m->h.dict_start_handler = start_fn;
  53 + this->m->h.dict_end_handler = end_fn;
  54 +}
  55 +
  56 +void
  57 +JSONHandler::addDictKeyHandler(
  58 + std::string const& key, std::shared_ptr<JSONHandler> dkh)
51 { 59 {
52 - return this->m->h.dict_handlers; 60 + this->m->h.dict_handlers[key] = dkh;
53 } 61 }
54 62
55 void 63 void
@@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr&lt;JSONHandler&gt; fdh) @@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr&lt;JSONHandler&gt; fdh)
59 } 67 }
60 68
61 void 69 void
62 -JSONHandler::addArrayHandler(std::shared_ptr<JSONHandler> ah) 70 +JSONHandler::addArrayHandlers(void_handler_t start_fn,
  71 + void_handler_t end_fn,
  72 + std::shared_ptr<JSONHandler> ah)
63 { 73 {
64 - this->m->h.array_handler = ah; 74 + this->m->h.array_start_handler = start_fn;
  75 + this->m->h.array_end_handler = end_fn;
  76 + this->m->h.array_item_handler = ah;
65 } 77 }
66 78
67 void 79 void
@@ -95,9 +107,9 @@ JSONHandler::handle(std::string const&amp; path, JSON j) @@ -95,9 +107,9 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
95 this->m->h.bool_handler(path, bvalue); 107 this->m->h.bool_handler(path, bvalue);
96 handled = true; 108 handled = true;
97 } 109 }
98 - if ((this->m->h.fallback_dict_handler.get() ||  
99 - (! this->m->h.dict_handlers.empty())) && j.isDictionary()) 110 + if (this->m->h.dict_start_handler && j.isDictionary())
100 { 111 {
  112 + this->m->h.dict_start_handler(path);
101 std::string path_base = path; 113 std::string path_base = path;
102 if (path_base != ".") 114 if (path_base != ".")
103 { 115 {
@@ -126,22 +138,19 @@ JSONHandler::handle(std::string const&amp; path, JSON j) @@ -126,22 +138,19 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
126 i->second->handle(path_base + k, v); 138 i->second->handle(path_base + k, v);
127 } 139 }
128 }); 140 });
129 -  
130 - // Set handled = true even if we didn't call any handlers.  
131 - // This dictionary could have been empty, but it's okay since  
132 - // it's a dictionary like it's supposed to be. 141 + this->m->h.dict_end_handler(path);
133 handled = true; 142 handled = true;
134 } 143 }
135 - if (this->m->h.array_handler.get()) 144 + if (this->m->h.array_start_handler && j.isArray())
136 { 145 {
  146 + this->m->h.array_start_handler(path);
137 size_t i = 0; 147 size_t i = 0;
138 j.forEachArrayItem([&i, &path, this](JSON v) { 148 j.forEachArrayItem([&i, &path, this](JSON v) {
139 - this->m->h.array_handler->handle( 149 + this->m->h.array_item_handler->handle(
140 path + "[" + QUtil::uint_to_string(i) + "]", v); 150 path + "[" + QUtil::uint_to_string(i) + "]", v);
141 ++i; 151 ++i;
142 }); 152 });
143 - // Set handled = true even if we didn't call any handlers.  
144 - // This could have been an empty array. 153 + this->m->h.array_end_handler(path);
145 handled = true; 154 handled = true;
146 } 155 }
147 156
libtests/json_handler.cc
@@ -28,6 +28,13 @@ static void print_json(std::string const&amp; path, JSON value) @@ -28,6 +28,13 @@ static void print_json(std::string const&amp; path, JSON value)
28 std::cout << path << ": json: " << value.unparse() << std::endl; 28 std::cout << path << ": json: " << value.unparse() << std::endl;
29 } 29 }
30 30
  31 +static JSONHandler::void_handler_t make_print_message(std::string msg)
  32 +{
  33 + return [msg](std::string const& path) {
  34 + std::cout << path << ": json: " << msg << std::endl;
  35 + };
  36 +}
  37 +
31 static void test_scalar() 38 static void test_scalar()
32 { 39 {
33 std::cout << "-- scalar --" << std::endl; 40 std::cout << "-- scalar --" << std::endl;
@@ -40,41 +47,50 @@ static void test_scalar() @@ -40,41 +47,50 @@ static void test_scalar()
40 static std::shared_ptr<JSONHandler> make_all_handler() 47 static std::shared_ptr<JSONHandler> make_all_handler()
41 { 48 {
42 auto h = std::make_shared<JSONHandler>(); 49 auto h = std::make_shared<JSONHandler>();
43 - auto& m = h->addDictHandlers(); 50 + h->addDictHandlers(
  51 + make_print_message("dict begin"),
  52 + make_print_message("dict end"));
44 auto h1 = std::make_shared<JSONHandler>(); 53 auto h1 = std::make_shared<JSONHandler>();
45 h1->addStringHandler(print_string); 54 h1->addStringHandler(print_string);
46 - m["one"] = h1; 55 + h->addDictKeyHandler("one", h1);
47 auto h2 = std::make_shared<JSONHandler>(); 56 auto h2 = std::make_shared<JSONHandler>();
48 h2->addNumberHandler(print_number); 57 h2->addNumberHandler(print_number);
49 - m["two"] = h2; 58 + h->addDictKeyHandler("two", h2);
50 auto h3 = std::make_shared<JSONHandler>(); 59 auto h3 = std::make_shared<JSONHandler>();
51 h3->addBoolHandler(print_bool); 60 h3->addBoolHandler(print_bool);
52 - m["three"] = h3; 61 + h->addDictKeyHandler("three", h3);
53 auto h4 = std::make_shared<JSONHandler>(); 62 auto h4 = std::make_shared<JSONHandler>();
54 h4->addAnyHandler(print_json); 63 h4->addAnyHandler(print_json);
55 - m["four"] = h4;  
56 - m["phour"] = h4; // share h4 64 + h->addDictKeyHandler("four", h4);
  65 + h->addDictKeyHandler("phour", h4); // share h4
57 auto h5 = std::make_shared<JSONHandler>(); 66 auto h5 = std::make_shared<JSONHandler>();
58 // Allow to be either string or bool 67 // Allow to be either string or bool
59 h5->addBoolHandler(print_bool); 68 h5->addBoolHandler(print_bool);
60 h5->addStringHandler(print_string); 69 h5->addStringHandler(print_string);
61 h5->addNullHandler(print_null); 70 h5->addNullHandler(print_null);
62 auto h5s = std::make_shared<JSONHandler>(); 71 auto h5s = std::make_shared<JSONHandler>();
63 - m["five"] = h5s;  
64 - h5s->addArrayHandler(h5); 72 + h->addDictKeyHandler("five", h5s);
  73 + h5s->addArrayHandlers(
  74 + make_print_message("array begin"),
  75 + make_print_message("array end"),
  76 + h5);
65 auto h6 = std::make_shared<JSONHandler>(); 77 auto h6 = std::make_shared<JSONHandler>();
66 - auto& m6 = h6->addDictHandlers(); 78 + h6->addDictHandlers(
  79 + make_print_message("dict begin"),
  80 + make_print_message("dict end"));
67 auto h6a = std::make_shared<JSONHandler>(); 81 auto h6a = std::make_shared<JSONHandler>();
68 - m6["a"] = h6a;  
69 - auto& m6a = h6a->addDictHandlers(); 82 + h6->addDictKeyHandler("a", h6a);
  83 + h6a->addDictHandlers(
  84 + make_print_message("dict begin"),
  85 + make_print_message("dict end"));
70 auto h6ab = std::make_shared<JSONHandler>(); 86 auto h6ab = std::make_shared<JSONHandler>();
71 - m6a["b"] = h6ab; 87 + h6a->addDictKeyHandler("b", h6ab);
72 auto h6ax = std::make_shared<JSONHandler>(); 88 auto h6ax = std::make_shared<JSONHandler>();
73 h6ax->addAnyHandler(print_json); 89 h6ax->addAnyHandler(print_json);
74 h6a->addFallbackDictHandler(h6ax); 90 h6a->addFallbackDictHandler(h6ax);
75 - m6["b"] = h6ab; // share 91 + h6->addDictKeyHandler("b", h6ab); // share
76 h6ab->addStringHandler(print_string); 92 h6ab->addStringHandler(print_string);
77 - m["six"] = h6; 93 + h->addDictKeyHandler("six", h6);
78 return h; 94 return h;
79 } 95 }
80 96
libtests/qtest/json_handler/json_handler.out
1 -- scalar -- 1 -- scalar --
2 .: string: potato 2 .: string: potato
3 -- all -- 3 -- all --
  4 +.: json: dict begin
  5 +.five: json: array begin
4 .five[0]: string: x 6 .five[0]: string: x
5 .five[1]: bool: false 7 .five[1]: bool: false
6 .five[2]: string: y 8 .five[2]: string: y
7 .five[3]: null 9 .five[3]: null
8 .five[4]: bool: true 10 .five[4]: bool: true
  11 +.five: json: array end
9 .four: json: [ 12 .four: json: [
10 "a", 13 "a",
11 1 14 1
12 ] 15 ]
13 .one: string: potato 16 .one: string: potato
14 .phour: json: null 17 .phour: json: null
  18 +.six: json: dict begin
  19 +.six.a: json: dict begin
15 .six.a.Q: json: "baaa" 20 .six.a.Q: json: "baaa"
16 .six.a.b: string: quack 21 .six.a.b: string: quack
  22 +.six.a: json: dict end
17 .six.b: string: moo 23 .six.b: string: moo
  24 +.six: json: dict end
18 .three: bool: true 25 .three: bool: true
19 .two: number: 3.14 26 .two: number: 3.14
  27 +.: json: dict end
20 -- errors -- 28 -- errors --
21 bad type at top: JSON handler: value at . is not of expected type 29 bad type at top: JSON handler: value at . is not of expected type
  30 +.: json: dict begin
22 unexpected key: JSON handler found unexpected key x in object at . 31 unexpected key: JSON handler found unexpected key x in object at .