1/* Copyright (C) 2014 SkySQL Ab, MariaDB Corporation Ab
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16class Json_writer;
17
18/*
19 Single_line_formatting_helper is used by Json_writer to do better formatting
20 of JSON documents.
21
22 The idea is to catch arrays that can be printed on one line:
23
24 arrayName : [ "boo", 123, 456 ]
25
26 and actually print them on one line. Arrrays that occupy too much space on
27 the line, or have nested members cannot be printed on one line.
28
29 We hook into JSON printing functions and try to detect the pattern. While
30 detecting the pattern, we will accumulate "boo", 123, 456 as strings.
31
32 Then,
33 - either the pattern is broken, and we print the elements out,
34 - or the pattern lasts till the end of the array, and we print the
35 array on one line.
36*/
37
38class Single_line_formatting_helper
39{
40 enum enum_state
41 {
42 INACTIVE,
43 ADD_MEMBER,
44 IN_ARRAY,
45 DISABLED
46 };
47
48 /*
49 This works like a finite automaton.
50
51 state=DISABLED means the helper is disabled - all on_XXX functions will
52 return false (which means "not handled") and do nothing.
53
54 +->-+
55 | v
56 INACTIVE ---> ADD_MEMBER ---> IN_ARRAY--->-+
57 ^ |
58 +------------------<--------------------+
59
60 For other states:
61 INACTIVE - initial state, we have nothing.
62 ADD_MEMBER - add_member() was called, the buffer has "member_name\0".
63 IN_ARRAY - start_array() was called.
64
65
66 */
67 enum enum_state state;
68 enum { MAX_LINE_LEN= 80 };
69 char buffer[80];
70
71 /* The data in the buffer is located between buffer[0] and buf_ptr */
72 char *buf_ptr;
73 uint line_len;
74
75 Json_writer *owner;
76public:
77 Single_line_formatting_helper() : state(INACTIVE), buf_ptr(buffer) {}
78
79 void init(Json_writer *owner_arg) { owner= owner_arg; }
80
81 bool on_add_member(const char *name);
82
83 bool on_start_array();
84 bool on_end_array();
85 void on_start_object();
86 // on_end_object() is not needed.
87
88 bool on_add_str(const char *str);
89
90 void flush_on_one_line();
91 void disable_and_flush();
92};
93
94
95/*
96 A class to write well-formed JSON documents. The documents are also formatted
97 for human readability.
98*/
99
100class Json_writer
101{
102public:
103 /* Add a member. We must be in an object. */
104 Json_writer& add_member(const char *name);
105
106 /* Add atomic values */
107 void add_str(const char* val);
108 void add_str(const String &str);
109
110 void add_ll(longlong val);
111 void add_size(longlong val);
112 void add_double(double val);
113 void add_bool(bool val);
114 void add_null();
115
116private:
117 void add_unquoted_str(const char* val);
118public:
119 /* Start a child object */
120 void start_object();
121 void start_array();
122
123 void end_object();
124 void end_array();
125
126 Json_writer() :
127 indent_level(0), document_start(true), element_started(false),
128 first_child(true)
129 {
130 fmt_helper.init(this);
131 }
132private:
133 // TODO: a stack of (name, bool is_object_or_array) elements.
134 int indent_level;
135 enum { INDENT_SIZE = 2 };
136
137 friend class Single_line_formatting_helper;
138 friend class Json_writer_nesting_guard;
139 bool document_start;
140 bool element_started;
141 bool first_child;
142
143 Single_line_formatting_helper fmt_helper;
144
145 void append_indent();
146 void start_element();
147 void start_sub_element();
148
149 //const char *new_member_name;
150public:
151 String output;
152};
153
154
155/*
156 RAII-based helper class to detect incorrect use of Json_writer.
157
158 The idea is that a function typically must leave Json_writer at the same
159 identation level as it was when it was invoked. Leaving it at a different
160 level typically means we forgot to close an object or an array
161
162 So, here is a way to guard
163 void foo(Json_writer *writer)
164 {
165 Json_writer_nesting_guard(writer);
166 .. do something with writer
167
168 // at the end of the function, ~Json_writer_nesting_guard() is called
169 // and it makes sure that the nesting is the same as when the function was
170 // entered.
171 }
172*/
173
174class Json_writer_nesting_guard
175{
176#ifdef DBUG_OFF
177public:
178 Json_writer_nesting_guard(Json_writer *) {}
179#else
180 Json_writer* writer;
181 int indent_level;
182public:
183 Json_writer_nesting_guard(Json_writer *writer_arg) :
184 writer(writer_arg),
185 indent_level(writer->indent_level)
186 {}
187
188 ~Json_writer_nesting_guard()
189 {
190 DBUG_ASSERT(indent_level == writer->indent_level);
191 }
192#endif
193};
194
195
196