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
16#include "mariadb.h"
17#include "sql_priv.h"
18#include "sql_string.h"
19
20#include "my_json_writer.h"
21
22void Json_writer::append_indent()
23{
24 if (!document_start)
25 output.append('\n');
26 for (int i=0; i< indent_level; i++)
27 output.append(' ');
28}
29
30void Json_writer::start_object()
31{
32 fmt_helper.on_start_object();
33
34 if (!element_started)
35 start_element();
36
37 output.append("{");
38 indent_level+=INDENT_SIZE;
39 first_child=true;
40 element_started= false;
41 document_start= false;
42}
43
44void Json_writer::start_array()
45{
46 if (fmt_helper.on_start_array())
47 return;
48
49 if (!element_started)
50 start_element();
51
52 output.append("[");
53 indent_level+=INDENT_SIZE;
54 first_child=true;
55 element_started= false;
56 document_start= false;
57}
58
59
60void Json_writer::end_object()
61{
62 indent_level-=INDENT_SIZE;
63 if (!first_child)
64 append_indent();
65 output.append("}");
66}
67
68
69void Json_writer::end_array()
70{
71 if (fmt_helper.on_end_array())
72 return;
73 indent_level-=INDENT_SIZE;
74 if (!first_child)
75 append_indent();
76 output.append("]");
77}
78
79
80Json_writer& Json_writer::add_member(const char *name)
81{
82 if (fmt_helper.on_add_member(name))
83 return *this; // handled
84
85 // assert that we are in an object
86 DBUG_ASSERT(!element_started);
87 start_element();
88
89 output.append('"');
90 output.append(name);
91 output.append("\": ");
92 return *this;
93}
94
95
96/*
97 Used by formatting helper to print something that is formatted by the helper.
98 We should only separate it from the previous element.
99*/
100
101void Json_writer::start_sub_element()
102{
103 //element_started= true;
104 if (first_child)
105 first_child= false;
106 else
107 output.append(',');
108
109 append_indent();
110}
111
112
113void Json_writer::start_element()
114{
115 element_started= true;
116
117 if (first_child)
118 first_child= false;
119 else
120 output.append(',');
121
122 append_indent();
123}
124
125void Json_writer::add_ll(longlong val)
126{
127 char buf[64];
128 my_snprintf(buf, sizeof(buf), "%lld", val);
129 add_unquoted_str(buf);
130}
131
132
133/* Add a memory size, printing in Kb, Kb, Gb if necessary */
134void Json_writer::add_size(longlong val)
135{
136 char buf[64];
137 if (val < 1024)
138 my_snprintf(buf, sizeof(buf), "%lld", val);
139 else if (val < 1024*1024*16)
140 {
141 /* Values less than 16MB are specified in KB for precision */
142 size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/1024);
143 strcpy(buf + len, "Kb");
144 }
145 else
146 {
147 size_t len= my_snprintf(buf, sizeof(buf), "%lld", val/(1024*1024));
148 strcpy(buf + len, "Mb");
149 }
150 add_str(buf);
151}
152
153
154void Json_writer::add_double(double val)
155{
156 char buf[64];
157 my_snprintf(buf, sizeof(buf), "%lg", val);
158 add_unquoted_str(buf);
159}
160
161
162void Json_writer::add_bool(bool val)
163{
164 add_unquoted_str(val? "true" : "false");
165}
166
167
168void Json_writer::add_null()
169{
170 add_unquoted_str("null");
171}
172
173
174void Json_writer::add_unquoted_str(const char* str)
175{
176 if (fmt_helper.on_add_str(str))
177 return;
178
179 if (!element_started)
180 start_element();
181
182 output.append(str);
183 element_started= false;
184}
185
186
187void Json_writer::add_str(const char *str)
188{
189 if (fmt_helper.on_add_str(str))
190 return;
191
192 if (!element_started)
193 start_element();
194
195 output.append('"');
196 output.append(str);
197 output.append('"');
198 element_started= false;
199}
200
201
202void Json_writer::add_str(const String &str)
203{
204 add_str(str.ptr());
205}
206
207
208bool Single_line_formatting_helper::on_add_member(const char *name)
209{
210 DBUG_ASSERT(state== INACTIVE || state == DISABLED);
211 if (state != DISABLED)
212 {
213 // remove everything from the array
214 buf_ptr= buffer;
215
216 //append member name to the array
217 size_t len= strlen(name);
218 if (len < MAX_LINE_LEN)
219 {
220 memcpy(buf_ptr, name, len);
221 buf_ptr+=len;
222 *(buf_ptr++)= 0;
223
224 line_len= owner->indent_level + (uint)len + 1;
225 state= ADD_MEMBER;
226 return true; // handled
227 }
228 }
229 return false; // not handled
230}
231
232
233bool Single_line_formatting_helper::on_start_array()
234{
235 if (state == ADD_MEMBER)
236 {
237 state= IN_ARRAY;
238 return true; // handled
239 }
240 else
241 {
242 if (state != DISABLED)
243 state= INACTIVE;
244 // TODO: what if we have accumulated some stuff already? shouldn't we
245 // flush it?
246 return false; // not handled
247 }
248}
249
250
251bool Single_line_formatting_helper::on_end_array()
252{
253 if (state == IN_ARRAY)
254 {
255 flush_on_one_line();
256 state= INACTIVE;
257 return true; // handled
258 }
259 return false; // not handled
260}
261
262
263void Single_line_formatting_helper::on_start_object()
264{
265 // Nested objects will not be printed on one line
266 disable_and_flush();
267}
268
269
270bool Single_line_formatting_helper::on_add_str(const char *str)
271{
272 if (state == IN_ARRAY)
273 {
274 size_t len= strlen(str);
275
276 // New length will be:
277 // "$string",
278 // quote + quote + comma + space = 4
279 if (line_len + len + 4 > MAX_LINE_LEN)
280 {
281 disable_and_flush();
282 return false; // didn't handle the last element
283 }
284
285 //append string to array
286 memcpy(buf_ptr, str, len);
287 buf_ptr+=len;
288 *(buf_ptr++)= 0;
289 line_len += (uint)len + 4;
290 return true; // handled
291 }
292
293 disable_and_flush();
294 return false; // not handled
295}
296
297
298/*
299 Append everything accumulated to the output on one line
300*/
301
302void Single_line_formatting_helper::flush_on_one_line()
303{
304 owner->start_sub_element();
305 char *ptr= buffer;
306 int nr= 0;
307 while (ptr < buf_ptr)
308 {
309 char *str= ptr;
310
311 if (nr == 0)
312 {
313 owner->output.append('"');
314 owner->output.append(str);
315 owner->output.append("\": ");
316 owner->output.append('[');
317 }
318 else
319 {
320 if (nr != 1)
321 owner->output.append(", ");
322 owner->output.append('"');
323 owner->output.append(str);
324 owner->output.append('"');
325 }
326 nr++;
327
328 while (*ptr!=0)
329 ptr++;
330 ptr++;
331 }
332 owner->output.append(']');
333 /* We've printed out the contents of the buffer, mark it as empty */
334 buf_ptr= buffer;
335}
336
337
338void Single_line_formatting_helper::disable_and_flush()
339{
340 if (state == DISABLED)
341 return;
342
343 bool start_array= (state == IN_ARRAY);
344 state= DISABLED;
345 // deactivate ourselves and flush all accumulated calls.
346 char *ptr= buffer;
347 int nr= 0;
348 while (ptr < buf_ptr)
349 {
350 char *str= ptr;
351 if (nr == 0)
352 {
353 owner->add_member(str);
354 if (start_array)
355 owner->start_array();
356 }
357 else
358 {
359 //if (nr == 1)
360 // owner->start_array();
361 owner->add_str(str);
362 }
363
364 nr++;
365 while (*ptr!=0)
366 ptr++;
367 ptr++;
368 }
369 buf_ptr= buffer;
370 state= INACTIVE;
371}
372
373