Commit 1db0a7ffcee6f6ae6bd3298a960665378d304fa1

Authored by Jay Berkenbilt
1 parent acf8d18b

JSONHandler: rework dictionary and array handlers

include/qpdf/JSONHandler.hh
... ... @@ -31,6 +31,11 @@
31 31 #include <stdexcept>
32 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 39 class JSONHandler
35 40 {
36 41 public:
... ... @@ -53,7 +58,8 @@ class JSONHandler
53 58 // certain type. JSONHandler::Error is thrown otherwise. Multiple
54 59 // handlers may be registered, which allows the object to be of
55 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 64 typedef std::function<void(
59 65 std::string const& path, JSON value)> json_handler_t;
... ... @@ -80,19 +86,18 @@ class JSONHandler
80 86 QPDF_DLL
81 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 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 94 QPDF_DLL
91 95 void addFallbackDictHandler(std::shared_ptr<JSONHandler>);
92 96  
93   - // Apply the given handler to each element of the array.
94 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 102 // Apply handlers recursively to a JSON object.
98 103 QPDF_DLL
... ... @@ -108,7 +113,12 @@ class JSONHandler
108 113 null_handler(nullptr),
109 114 string_handler(nullptr),
110 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 127 string_handler_t string_handler;
118 128 string_handler_t number_handler;
119 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 135 std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers;
121 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 140 class Members
... ...
libqpdf/JSONHandler.cc
... ... @@ -46,10 +46,18 @@ JSONHandler::addBoolHandler(bool_handler_t fn)
46 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 63 void
... ... @@ -59,9 +67,13 @@ JSONHandler::addFallbackDictHandler(std::shared_ptr&lt;JSONHandler&gt; fdh)
59 67 }
60 68  
61 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 79 void
... ... @@ -95,9 +107,9 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
95 107 this->m->h.bool_handler(path, bvalue);
96 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 113 std::string path_base = path;
102 114 if (path_base != ".")
103 115 {
... ... @@ -126,22 +138,19 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
126 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 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 147 size_t i = 0;
138 148 j.forEachArrayItem([&i, &path, this](JSON v) {
139   - this->m->h.array_handler->handle(
  149 + this->m->h.array_item_handler->handle(
140 150 path + "[" + QUtil::uint_to_string(i) + "]", v);
141 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 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 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 38 static void test_scalar()
32 39 {
33 40 std::cout << "-- scalar --" << std::endl;
... ... @@ -40,41 +47,50 @@ static void test_scalar()
40 47 static std::shared_ptr<JSONHandler> make_all_handler()
41 48 {
42 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 53 auto h1 = std::make_shared<JSONHandler>();
45 54 h1->addStringHandler(print_string);
46   - m["one"] = h1;
  55 + h->addDictKeyHandler("one", h1);
47 56 auto h2 = std::make_shared<JSONHandler>();
48 57 h2->addNumberHandler(print_number);
49   - m["two"] = h2;
  58 + h->addDictKeyHandler("two", h2);
50 59 auto h3 = std::make_shared<JSONHandler>();
51 60 h3->addBoolHandler(print_bool);
52   - m["three"] = h3;
  61 + h->addDictKeyHandler("three", h3);
53 62 auto h4 = std::make_shared<JSONHandler>();
54 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 66 auto h5 = std::make_shared<JSONHandler>();
58 67 // Allow to be either string or bool
59 68 h5->addBoolHandler(print_bool);
60 69 h5->addStringHandler(print_string);
61 70 h5->addNullHandler(print_null);
62 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 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 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 86 auto h6ab = std::make_shared<JSONHandler>();
71   - m6a["b"] = h6ab;
  87 + h6a->addDictKeyHandler("b", h6ab);
72 88 auto h6ax = std::make_shared<JSONHandler>();
73 89 h6ax->addAnyHandler(print_json);
74 90 h6a->addFallbackDictHandler(h6ax);
75   - m6["b"] = h6ab; // share
  91 + h6->addDictKeyHandler("b", h6ab); // share
76 92 h6ab->addStringHandler(print_string);
77   - m["six"] = h6;
  93 + h->addDictKeyHandler("six", h6);
78 94 return h;
79 95 }
80 96  
... ...
libtests/qtest/json_handler/json_handler.out
1 1 -- scalar --
2 2 .: string: potato
3 3 -- all --
  4 +.: json: dict begin
  5 +.five: json: array begin
4 6 .five[0]: string: x
5 7 .five[1]: bool: false
6 8 .five[2]: string: y
7 9 .five[3]: null
8 10 .five[4]: bool: true
  11 +.five: json: array end
9 12 .four: json: [
10 13 "a",
11 14 1
12 15 ]
13 16 .one: string: potato
14 17 .phour: json: null
  18 +.six: json: dict begin
  19 +.six.a: json: dict begin
15 20 .six.a.Q: json: "baaa"
16 21 .six.a.b: string: quack
  22 +.six.a: json: dict end
17 23 .six.b: string: moo
  24 +.six: json: dict end
18 25 .three: bool: true
19 26 .two: number: 3.14
  27 +.: json: dict end
20 28 -- errors --
21 29 bad type at top: JSON handler: value at . is not of expected type
  30 +.: json: dict begin
22 31 unexpected key: JSON handler found unexpected key x in object at .
... ...