source: trunk/tools/runonphone/symbianutils/json.cpp@ 932

Last change on this file since 932 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 13.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "json.h"
43
44#ifdef TODO_USE_CREATOR
45#include <utils/qtcassert.h>
46#endif // TODO_USE_CREATOR
47
48#include <QtCore/QByteArray>
49#include <QtCore/QTextStream>
50#include <QtCore/QDebug>
51#include <QtCore/QStringList>
52
53#include <ctype.h>
54
55//#define DEBUG_JASON
56#ifdef DEBUG_JASON
57#define JDEBUG(s) qDebug() << s
58#else
59#define JDEBUG(s)
60#endif
61
62namespace tcftrk {
63
64static void skipSpaces(const char *&from, const char *to)
65{
66 while (from != to && isspace(*from))
67 ++from;
68}
69
70QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
71{
72 return os << mi.toString();
73}
74
75void JsonValue::parsePair(const char *&from, const char *to)
76{
77 skipSpaces(from, to);
78 JDEBUG("parsePair: " << QByteArray(from, to - from));
79 m_name = parseCString(from, to);
80 skipSpaces(from, to);
81 while (from < to && *from != ':') {
82 JDEBUG("not a colon" << *from);
83 ++from;
84 }
85 ++from;
86 parseValue(from, to);
87 skipSpaces(from, to);
88}
89
90QByteArray JsonValue::parseNumber(const char *&from, const char *to)
91{
92 QByteArray result;
93 if (from < to && *from == '-') // Leading '-'.
94 result.append(*from++);
95 while (from < to && *from >= '0' && *from <= '9')
96 result.append(*from++);
97 return result;
98}
99
100QByteArray JsonValue::parseCString(const char *&from, const char *to)
101{
102 QByteArray result;
103 JDEBUG("parseCString: " << QByteArray(from, to - from));
104 if (*from != '"') {
105 qDebug() << "JSON Parse Error, double quote expected";
106 ++from; // So we don't hang
107 return QByteArray();
108 }
109 const char *ptr = from;
110 ++ptr;
111 while (ptr < to) {
112 if (*ptr == '"') {
113 ++ptr;
114 result = QByteArray(from + 1, ptr - from - 2);
115 break;
116 }
117 if (*ptr == '\\') {
118 ++ptr;
119 if (ptr == to) {
120 qDebug() << "JSON Parse Error, unterminated backslash escape";
121 from = ptr; // So we don't hang
122 return QByteArray();
123 }
124 }
125 ++ptr;
126 }
127 from = ptr;
128
129 int idx = result.indexOf('\\');
130 if (idx >= 0) {
131 char *dst = result.data() + idx;
132 const char *src = dst + 1, *end = result.data() + result.length();
133 do {
134 char c = *src++;
135 switch (c) {
136 case 'a': *dst++ = '\a'; break;
137 case 'b': *dst++ = '\b'; break;
138 case 'f': *dst++ = '\f'; break;
139 case 'n': *dst++ = '\n'; break;
140 case 'r': *dst++ = '\r'; break;
141 case 't': *dst++ = '\t'; break;
142 case 'v': *dst++ = '\v'; break;
143 case '"': *dst++ = '"'; break;
144 case '\\': *dst++ = '\\'; break;
145 default:
146 {
147 int chars = 0;
148 uchar prod = 0;
149 forever {
150 if (c < '0' || c > '7') {
151 --src;
152 break;
153 }
154 prod = prod * 8 + c - '0';
155 if (++chars == 3 || src == end)
156 break;
157 c = *src++;
158 }
159 if (!chars) {
160 qDebug() << "JSON Parse Error, unrecognized backslash escape";
161 return QByteArray();
162 }
163 *dst++ = prod;
164 }
165 }
166 while (src != end) {
167 char c = *src++;
168 if (c == '\\')
169 break;
170 *dst++ = c;
171 }
172 } while (src != end);
173 *dst = 0;
174 result.truncate(dst - result.data());
175 }
176
177 JDEBUG("parseCString, got " << result);
178 return result;
179}
180
181
182
183void JsonValue::parseValue(const char *&from, const char *to)
184{
185 JDEBUG("parseValue: " << QByteArray(from, to - from));
186 switch (*from) {
187 case '{':
188 parseObject(from, to);
189 break;
190 case 't':
191 if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
192 m_data = QByteArray(from, 4);
193 from += m_data.size();
194 m_type = Boolean;
195 }
196 break;
197 case 'f':
198 if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
199 m_data = QByteArray(from, 5);
200 from += m_data.size();
201 m_type = Boolean;
202 }
203 break;
204 case 'n':
205 if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
206 m_data = QByteArray(from, 4);
207 from += m_data.size();
208 m_type = NullObject;
209 }
210 break;
211 case '[':
212 parseArray(from, to);
213 break;
214 case '"':
215 m_type = String;
216 m_data = parseCString(from, to);
217 break;
218 case '0': case '1': case '2': case '3': case '4':
219 case '5': case '6': case '7': case '8': case '9':
220 case '-':
221 m_type = Number;
222 m_data = parseNumber(from, to);
223 default:
224 break;
225 }
226}
227
228void JsonValue::parseObject(const char *&from, const char *to)
229{
230 JDEBUG("parseObject: " << QByteArray(from, to - from));
231#ifdef TODO_USE_CREATOR
232 QTC_ASSERT(*from == '{', /**/);
233#endif
234 ++from;
235 m_type = Object;
236 while (from < to) {
237 if (*from == '}') {
238 ++from;
239 break;
240 }
241 JsonValue child;
242 child.parsePair(from, to);
243 if (!child.isValid())
244 return;
245 m_children += child;
246 if (*from == ',')
247 ++from;
248 }
249}
250
251void JsonValue::parseArray(const char *&from, const char *to)
252{
253 JDEBUG("parseArray: " << QByteArray(from, to - from));
254#ifdef TODO_USE_CREATOR
255 QTC_ASSERT(*from == '[', /**/);
256#endif
257 ++from;
258 m_type = Array;
259 while (from < to) {
260 if (*from == ']') {
261 ++from;
262 break;
263 }
264 JsonValue child;
265 child.parseValue(from, to);
266 if (child.isValid())
267 m_children += child;
268 if (*from == ',')
269 ++from;
270 }
271}
272
273void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
274{
275 if (content.isEmpty())
276 return;
277 JsonValue child;
278 child.m_type = String;
279 child.m_name = name;
280 child.m_data = content;
281 m_children += child;
282 if (m_type == Invalid)
283 m_type = Object;
284}
285
286static QByteArray ind(int indent)
287{
288 return QByteArray(2 * indent, ' ');
289}
290
291void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
292{
293 for (int i = 0; i < m_children.size(); ++i) {
294 if (i != 0) {
295 *str += ',';
296 if (multiline)
297 *str += '\n';
298 }
299 if (multiline)
300 *str += ind(indent);
301 *str += m_children.at(i).toString(multiline, indent);
302 }
303}
304
305class MyString : public QString {
306public:
307 ushort at(int i) const { return constData()[i].unicode(); }
308};
309
310template<class ST, typename CT>
311inline ST escapeCStringTpl(const ST &ba)
312{
313 ST ret;
314 ret.reserve(ba.length() * 2);
315 for (int i = 0; i < ba.length(); ++i) {
316 CT c = ba.at(i);
317 switch (c) {
318 case '\\': ret += "\\\\"; break;
319 case '\a': ret += "\\a"; break;
320 case '\b': ret += "\\b"; break;
321 case '\f': ret += "\\f"; break;
322 case '\n': ret += "\\n"; break;
323 case '\r': ret += "\\r"; break;
324 case '\t': ret += "\\t"; break;
325 case '\v': ret += "\\v"; break;
326 case '"': ret += "\\\""; break;
327 default:
328 if (c < 32 || c == 127) {
329 ret += '\\';
330 ret += '0' + (c >> 6);
331 ret += '0' + ((c >> 3) & 7);
332 ret += '0' + (c & 7);
333 } else {
334 ret += c;
335 }
336 }
337 }
338 return ret;
339}
340
341QString JsonValue::escapeCString(const QString &ba)
342{
343 return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
344}
345
346QByteArray JsonValue::escapeCString(const QByteArray &ba)
347{
348 return escapeCStringTpl<QByteArray, uchar>(ba);
349}
350
351QByteArray JsonValue::toString(bool multiline, int indent) const
352{
353 QByteArray result;
354 switch (m_type) {
355 case Invalid:
356 if (multiline)
357 result += ind(indent) + "Invalid\n";
358 else
359 result += "Invalid";
360 break;
361 case String:
362 if (!m_name.isEmpty())
363 result += m_name + "=";
364 result += '"' + escapeCString(m_data) + '"';
365 break;
366 case Number:
367 if (!m_name.isEmpty())
368 result += '"' + m_name + "\":";
369 result += m_data;
370 break;
371 case Boolean:
372 case NullObject:
373 if (!m_name.isEmpty())
374 result += '"' + m_name + "\":";
375 result += m_data;
376 break;
377 case Object:
378 if (!m_name.isEmpty())
379 result += m_name + '=';
380 if (multiline) {
381 result += "{\n";
382 dumpChildren(&result, multiline, indent + 1);
383 result += '\n' + ind(indent) + "}";
384 } else {
385 result += "{";
386 dumpChildren(&result, multiline, indent + 1);
387 result += "}";
388 }
389 break;
390 case Array:
391 if (!m_name.isEmpty())
392 result += m_name + "=";
393 if (multiline) {
394 result += "[\n";
395 dumpChildren(&result, multiline, indent + 1);
396 result += '\n' + ind(indent) + "]";
397 } else {
398 result += "[";
399 dumpChildren(&result, multiline, indent + 1);
400 result += "]";
401 }
402 break;
403 }
404 return result;
405}
406
407void JsonValue::fromString(const QByteArray &ba)
408{
409 const char *from = ba.constBegin();
410 const char *to = ba.constEnd();
411 parseValue(from, to);
412}
413
414JsonValue JsonValue::findChild(const char *name) const
415{
416 for (int i = 0; i < m_children.size(); ++i)
417 if (m_children.at(i).m_name == name)
418 return m_children.at(i);
419 return JsonValue();
420}
421
422void JsonInputStream::appendCString(const char *s)
423{
424 m_target.append('"');
425 for (const char *p = s; *p; p++) {
426 if (*p == '"' || *p == '\\')
427 m_target.append('\\');
428 m_target.append(*p);
429 }
430 m_target.append('"');
431}
432
433void JsonInputStream::appendString(const QString &in)
434{
435 if (in.isEmpty()) {
436 m_target.append("\"\"");
437 return;
438 }
439
440 const QChar doubleQuote('"');
441 const QChar backSlash('\\');
442 QString rc;
443 const int inSize = in.size();
444 rc.reserve(in.size() + 5);
445 rc.append(doubleQuote);
446 for (int i = 0; i < inSize; i++) {
447 const QChar c = in.at(i);
448 if (c == doubleQuote || c == backSlash)
449 rc.append(backSlash);
450 rc.append(c);
451 }
452 rc.append(doubleQuote);
453 m_target.append(rc.toUtf8());
454 return;
455}
456
457JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
458{
459 m_target.append('[');
460 const int count = in.size();
461 for (int i = 0 ; i < count; i++) {
462 if (i)
463 m_target.append(',');
464 appendString(in.at(i));
465 }
466 m_target.append(']');
467 return *this;
468}
469
470JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
471{
472 m_target.append('[');
473 const int count = ba.size();
474 for (int i = 0 ; i < count; i++) {
475 if (i)
476 m_target.append(',');
477 appendCString(ba.at(i).constData());
478 }
479 m_target.append(']');
480 return *this;
481}
482
483JsonInputStream &JsonInputStream::operator<<(bool b)
484{
485 m_target.append(b ? "true" : "false");
486 return *this;
487}
488
489} // namespace tcftrk
490
Note: See TracBrowser for help on using the repository browser.