1 /********************************************************************************
2 *                                                                               *
3 *                      J S O N   R e a d e r  &  W r i t e r                    *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2013,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #ifndef FXJSON_H
22 #define FXJSON_H
23 
24 namespace FX {
25 
26 
27 /**
28 * The FXJSON serializer loads or saves an FXVariant to a JSON text file.
29 * Since FXVariant can contain an arbitrarily complex data structure, this
30 * provides applications with a convenient way to load and save state information
31 * in a well-defined and human-readable file format.
32 * The base class implements serialization/deserialization to/from an external
33 * buffer.
34 * Subclasses FXJSONFile and FXJSONString serialize from/to strings and disk-
35 * based files, respectively.
36 * The new JSON5 standard may also be parsed, allowing for single- and multi-line
37 * nested comments to be embedded in the input.
38 * Syntax errors in the input cause the parser to return an error, and allow
39 * diagnosis of the problem and its location in the file by line number, column
40 * number, and byte-offset from the start of the file.
41 * When writing a json stream, the generated output may be formatter in different
42 * ways. The flow-mode controls the overall layout of the resulting text output;
43 * when flow is set to Stream, all output is generated with no formatting to
44 * improve human legibility.  This is the most space-friendly format possible.
45 * If flow is set to Compact, a human readable, compact format, aiming to
46 * maximize the amount of information on each line is generated.
47 * When flow is set to Pretty, a nicely indented, but extremely airy output
48 * results, and the resulting document will contain many, many lines with
49 * little data.
50 * Numeric values are printed with configurable precision; (default=15 digits
51 * which results in minimal information loss for real numbers).
52 * For Pretty flow format, output may be indented in multiples of the indent
53 * level (default=2).  Depending on flow setting, lines may be wrapped at a
54 * maximum number of columns (default=80).
55 * Output strings containing reserved characters may be escaped; for UTF8
56 * characters, there are 3 options for escaping.  When escape mode is 0, UTF8
57 * characters are passed unescaped.  In escape mode 1, UTF8 characters are
58 * escaped as \xXX, and in escape mode 2, UTF8 will be escaed using Unicode
59 * escape sequences \uXXXX or \uXXXX\uXXXX (two surrogate-pairs  escape codes
60 * for code points exceeding 16 bits).
61 * The default setting is to allow UTF8 characters in the output, but be aware
62 * that such outputs need UTF8-capable viewer software to be rendered properly.
63 */
64 class FXAPI FXJSON {
65 public:
66   enum Error {
67     ErrOK,              /// No errors
68     ErrSave,            /// Unable to save
69     ErrLoad,            /// Unable to load
70     ErrToken,           /// Illegal token
71     ErrColon,           /// Expected colon ':'
72     ErrComma,           /// Expected comma ','
73     ErrBracket,         /// Expected closing bracket
74     ErrBrace,           /// Expected closing brace
75     ErrQuotes,          /// Expected closing quotes
76     ErrQuote,           /// Expected closing quote
77     ErrNumber,          /// Numeric conversion
78     ErrEnd              /// Unexpected end of file
79     };
80   enum Flow {
81     Stream,             /// Stream-of-consciousness output
82     Compact,            /// Compact, human readable output (default)
83     Pretty              /// Pretty printed, indented output
84     };
85   enum Direction {
86     Stop = 0,           /// Not active
87     Save = 1,           /// Save to device
88     Load = 2            /// Load from device
89     };
90 protected:
91   FXchar     *begptr;           // Text buffer begin ptr
92   FXchar     *endptr;           // Text buffer end ptr
93   FXchar     *wptr;             // Text buffer write ptr
94   FXchar     *rptr;             // Text buffer read ptr
95   FXchar     *sptr;             // Text buffer scan ptr
96   FXlong      offset;           // Position from start
97   FXint       column;           // Column number
98   FXint       indent;           // Indent level
99   FXint       line;             // Line number
100   Direction   dir;              // Direction
101   FXint       token;            // Token
102   FXint       wrap;             // Line wrap column
103   FXuchar     flow;             // Output flow
104   FXuchar     prec;             // Float precision
105   FXuchar     fmt;              // Float format
106   FXuchar     esc;              // Escape mode
107   FXuchar     dent;             // Indentation amount
108 private:
109   FXint next();
110   FXbool need(FXival count);
111   FXbool emit(const FXchar* str,FXival count);
112   FXbool emit(FXchar ch,FXival count);
113   Error loadString(FXString& str);
114   Error loadIdent(FXString& str);
115   Error loadMap(FXVariant& var);
116   Error loadArray(FXVariant& var);
117   Error loadVariant(FXVariant& var);
118   Error saveString(const FXString& str);
119   Error saveIdent(const FXString& str);
120   Error saveMap(const FXVariant& var);
121   Error saveArray(const FXVariant& var);
122   Error saveVariant(const FXVariant& var);
123 private:
124   static const FXchar *const errors[];
125 private:
126   FXJSON(const FXJSON&);
127   FXJSON &operator=(const FXJSON&);
128 public:
129 
130   /**
131   * Initialize JSON serializer with no buffer.
132   */
133   FXJSON();
134 
135   /**
136   * Construct JSON serializer with given buffer of size, and open it for
137   * direction d.
138   */
139   FXJSON(FXchar* buffer,FXuval sz=8192,Direction d=Load);
140 
141   /**
142   * Open JSON parse buffer with size and direction.
143   */
144   FXbool open(FXchar* buffer=NULL,FXuval sz=8192,Direction d=Load);
145 
146   /**
147   * Return direction in effect.
148   */
direction()149   Direction direction() const { return dir; }
150 
151   /**
152   * Return size of parse buffer
153   */
size()154   FXuval size() const { return endptr-begptr; }
155 
156   /**
157   * Return current line number.
158   */
getLine()159   FXint getLine() const { return line; }
160 
161   /**
162   * Return current column number.
163   */
getColumn()164   FXint getColumn() const { return column; }
165 
166   /**
167   * Return offset from begin of file.
168   */
getOffset()169   FXlong getOffset() const { return offset; }
170 
171   /**
172   * Load a variant from JSON stream.
173   * Return false if stream wasn't opened for loading, or syntax error.
174   */
175   Error load(FXVariant& variant);
176 
177   /**
178   * Save a variant to JSON stream.
179   * Return false if stream wasn't opened for saving, or disk was full.
180   */
181   Error save(const FXVariant& variant);
182 
183   /// Returns error code for given error
getError(Error err)184   static const FXchar* getError(Error err){ return errors[err]; }
185 
186   /**
187   * Floating point output precision control.
188   * This controls the number of significant digits written to
189   * the output.  The default is 15.
190   */
setNumericPrecision(FXint p)191   void setNumericPrecision(FXint p){ prec=p; }
getNumericPrecision()192   FXint getNumericPrecision() const { return prec; }
193 
194   /**
195   * Floating point output format control.
196   * The format mode is interpreted as follows:
197   *
198   *   0   No exponent.
199   *   1   Exponent.
200   *   2   Output exponent when required.
201   *
202   * The default mode is 2.
203   */
setNumericFormat(FXint f)204   void setNumericFormat(FXint f){ fmt=f; }
getNumericFormat()205   FXint getNumericFormat() const { return fmt; }
206 
207   /**
208   * Change output flow format (Stream, Compact, Pretty).
209   * Stream is the most compact, but pretty much unreadable by humans; it
210   * aims to be compact.
211   * Compact is very human-readable while at the same time using minimum number
212   * of lines to represent the output.
213   * Pretty will print one item on each line, with indentation.  It is very easily
214   * readable but produces large numbers of text lines.
215   */
setOutputFlow(FXint f)216   void setOutputFlow(FXint f){ flow=f; }
getOutputFlow()217   FXint getOutputFlow() const { return flow; }
218 
219   /**
220   * Change indentation level for pretty print flow, the amount of
221   * indentation applied for each level.
222   */
setIndentation(FXint d)223   void setIndentation(FXint d){ dent=d; }
getIndentation()224   FXint getIndentation() const { return dent; }
225 
226   /**
227   * Change column at which lines are wrapped.
228   */
setLineWrap(FXint w)229   void setLineWrap(FXint w){ wrap=w; }
getLineWrap()230   FXint getLineWrap() const { return wrap; }
231 
232   /**
233   * Change string escape mode.
234   * The escape mode is interpreted as follows:
235   *
236   *  0  Don't escape unicode in strings;
237   *  1  Escape unicode as \xXX
238   *  2  Escape unicode as \uXXXX or \uXXXX\uXXXX.
239   *
240   * Default is to escape control characters only.
241   */
setEscapeMode(FXint e)242   void setEscapeMode(FXint e){ esc=e; }
getEscapeMode()243   FXint getEscapeMode() const { return esc; }
244 
245   /**
246   * Read at least count bytes into buffer; return bytes available, or -1 for error.
247   */
248   virtual FXival fill(FXival count);
249 
250   /**
251   * Write at least count bytes from buffer; return space available, or -1 for error.
252   */
253   virtual FXival flush(FXival count);
254 
255   /**
256   * Close stream and delete buffer, if owned.
257   */
258   FXbool close();
259 
260   /**
261   * Close JSON stream and clean up.
262   */
263   virtual ~FXJSON();
264   };
265 
266 }
267 
268 #endif
269