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 02110-1335 USA */
15 
16 class 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 
38 class 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;
76 public:
Single_line_formatting_helper()77   Single_line_formatting_helper() : state(INACTIVE), buf_ptr(buffer) {}
78 
init(Json_writer * owner_arg)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 
100 class Json_writer
101 {
102 public:
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_ull(ulonglong val);
112   void add_size(longlong val);
113   void add_double(double val);
114   void add_bool(bool val);
115   void add_null();
116 
117 private:
118   void add_unquoted_str(const char* val);
119 public:
120   /* Start a child object */
121   void start_object();
122   void start_array();
123 
124   void end_object();
125   void end_array();
126 
Json_writer()127   Json_writer() :
128     indent_level(0), document_start(true), element_started(false),
129     first_child(true)
130   {
131     fmt_helper.init(this);
132   }
133 private:
134   // TODO: a stack of (name, bool is_object_or_array) elements.
135   int indent_level;
136   enum { INDENT_SIZE = 2 };
137 
138   friend class Single_line_formatting_helper;
139   friend class Json_writer_nesting_guard;
140   bool document_start;
141   bool element_started;
142   bool first_child;
143 
144   Single_line_formatting_helper fmt_helper;
145 
146   void append_indent();
147   void start_element();
148   void start_sub_element();
149 
150   //const char *new_member_name;
151 public:
152   String output;
153 };
154 
155 
156 /*
157   RAII-based helper class to detect incorrect use of Json_writer.
158 
159   The idea is that a function typically must leave Json_writer at the same
160   identation level as it was when it was invoked. Leaving it at a different
161   level typically means we forgot to close an object or an array
162 
163   So, here is a way to guard
164   void foo(Json_writer *writer)
165   {
166     Json_writer_nesting_guard(writer);
167     .. do something with writer
168 
169     // at the end of the function, ~Json_writer_nesting_guard() is called
170     // and it makes sure that the nesting is the same as when the function was
171     // entered.
172   }
173 */
174 
175 class Json_writer_nesting_guard
176 {
177 #ifdef DBUG_OFF
178 public:
Json_writer_nesting_guard(Json_writer *)179   Json_writer_nesting_guard(Json_writer *) {}
180 #else
181   Json_writer* writer;
182   int indent_level;
183 public:
184   Json_writer_nesting_guard(Json_writer *writer_arg) :
185     writer(writer_arg),
186     indent_level(writer->indent_level)
187   {}
188 
189   ~Json_writer_nesting_guard()
190   {
191     DBUG_ASSERT(indent_level == writer->indent_level);
192   }
193 #endif
194 };
195 
196 
197