tools/runonphone/symbianutils/json.cpp
changeset 33 3e2da88830cd
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    62 namespace tcftrk {
       
    63 
       
    64 static void skipSpaces(const char *&from, const char *to)
       
    65 {
       
    66     while (from != to && isspace(*from))
       
    67         ++from;
       
    68 }
       
    69 
       
    70 QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
       
    71 {
       
    72     return os << mi.toString();
       
    73 }
       
    74 
       
    75 void 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 
       
    90 QByteArray 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 
       
   100 QByteArray 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 
       
   183 void 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 
       
   228 void 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 
       
   251 void 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 
       
   273 void 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 
       
   286 static QByteArray ind(int indent)
       
   287 {
       
   288     return QByteArray(2 * indent, ' ');
       
   289 }
       
   290 
       
   291 void 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 
       
   305 class MyString : public QString {
       
   306 public:
       
   307     ushort at(int i) const { return constData()[i].unicode(); }
       
   308 };
       
   309 
       
   310 template<class ST, typename CT>
       
   311 inline 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 
       
   341 QString JsonValue::escapeCString(const QString &ba)
       
   342 {
       
   343     return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
       
   344 }
       
   345 
       
   346 QByteArray JsonValue::escapeCString(const QByteArray &ba)
       
   347 {
       
   348     return escapeCStringTpl<QByteArray, uchar>(ba);
       
   349 }
       
   350 
       
   351 QByteArray 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 
       
   407 void JsonValue::fromString(const QByteArray &ba)
       
   408 {
       
   409     const char *from = ba.constBegin();
       
   410     const char *to = ba.constEnd();
       
   411     parseValue(from, to);
       
   412 }
       
   413 
       
   414 JsonValue 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 
       
   422 void 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 
       
   433 void 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 
       
   457 JsonInputStream &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 
       
   470 JsonInputStream &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 
       
   483 JsonInputStream &JsonInputStream::operator<<(bool b)
       
   484 {
       
   485     m_target.append(b ? "true" : "false");
       
   486     return *this;
       
   487 }
       
   488 
       
   489 } // namespace tcftrk
       
   490