]> git.cworth.org Git - apitrace/blob - thirdparty/qjson/serializer.cpp
Update bundled QJson
[apitrace] / thirdparty / qjson / serializer.cpp
1 /* This file is part of qjson
2   *
3   * Copyright (C) 2009 Till Adam <adam@kde.org>
4   * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
5   *
6   * This library is free software; you can redistribute it and/or
7   * modify it under the terms of the GNU Lesser General Public
8   * License version 2.1, as published by the Free Software Foundation.
9   * 
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public License
17   * along with this library; see the file COPYING.LIB.  If not, write to
18   * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19   * Boston, MA 02110-1301, USA.
20   */
21
22 #include "serializer.h"
23
24 #include <QtCore/QDataStream>
25 #include <QtCore/QStringList>
26 #include <QtCore/QVariant>
27
28 #include <cmath>
29
30 #ifdef _MSC_VER  // using MSVC compiler
31 #include <float.h>
32 #endif
33
34 using namespace QJson;
35
36 class Serializer::SerializerPrivate {
37   public:
38     SerializerPrivate() :
39       specialNumbersAllowed(false),
40       indentMode(QJson::IndentNone),
41       doublePrecision(6) {
42       }
43     bool specialNumbersAllowed;
44     IndentMode indentMode;
45     int doublePrecision;
46     QByteArray buildIndent(int spaces);
47     QByteArray serialize( const QVariant &v, int reserved = 0);
48     QString sanitizeString( QString str );
49     QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
50 };
51
52 QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
53   QByteArray res;
54   Q_FOREACH( const QByteArray& i, list ) {
55     if ( !res.isEmpty() )
56       res += sep;
57     res += i;
58   }
59   return res;
60 }
61
62 QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, int reserved )
63 {
64   QByteArray str;
65   bool error = false;
66   QByteArray indent;
67
68   if ( ! v.isValid() ) { // invalid or null?
69     str = "null";
70   } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
71     const QVariantList list = v.toList();
72     QList<QByteArray> values;
73     Q_FOREACH( const QVariant& var, list )
74     {
75       reserved++;
76       QByteArray serializedValue = serialize( var,reserved );
77       reserved--;
78       if ( serializedValue.isNull() ) {
79         error = true;
80         break;
81       }
82       values << serializedValue;
83     }
84
85     if (indentMode == QJson::IndentMinimum) {
86       QByteArray indent = buildIndent(reserved - 1);
87       str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
88     }
89     else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
90       QByteArray indent = buildIndent(reserved);
91       str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
92     }
93     else if (indentMode == QJson::IndentCompact) {
94       str = "[" + join( values, "," ) + "]";
95     }
96     else {
97       str = "[ " + join( values, ", " ) + " ]";
98     }
99
100   } else if ( v.type() == QVariant::Map ) { // variant is a map?
101     const QVariantMap vmap = v.toMap();
102     QMapIterator<QString, QVariant> it( vmap );
103
104     if (indentMode == QJson::IndentMinimum) {
105       QByteArray indent = buildIndent(reserved);
106       str = indent + "{ ";
107     }
108     else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
109       QByteArray indent = buildIndent(reserved);
110       QByteArray nextindent = buildIndent(reserved + 1);
111       str = indent + "{\n" + nextindent;
112     }
113     else if (indentMode == QJson::IndentCompact) {
114       str = "{";
115     }
116     else {
117       str = "{ ";
118     }
119
120     QList<QByteArray> pairs;
121     while ( it.hasNext() ) {
122       it.next();
123       reserved++;
124       QByteArray serializedValue = serialize( it.value() , reserved);
125       reserved--;
126       if ( serializedValue.isNull() ) {
127         error = true;
128         break;
129       }
130       QByteArray key   = sanitizeString( it.key() ).toUtf8();
131       QByteArray value = serializedValue;
132       if (indentMode == QJson::IndentCompact) {
133         pairs << key + ":" + value;
134       } else {
135         pairs << key + " : " + value;
136       }
137     }
138
139     if (indentMode == QJson::IndentFull) {
140       QByteArray indent = buildIndent(reserved + 1);
141       str += join( pairs, ",\n" + indent);
142     }
143     else if (indentMode == QJson::IndentCompact) {
144       str += join( pairs, "," );
145     }
146     else {
147       str += join( pairs, ", " );
148     }
149
150     if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
151       QByteArray indent = buildIndent(reserved);
152       str += "\n" + indent + "}";
153     }
154     else if (indentMode == QJson::IndentCompact) {
155       str += "}";
156     }
157     else {
158       str += " }";
159     }
160
161   } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
162     str = sanitizeString( v.toString() ).toUtf8();
163   } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
164     const double value = v.toDouble();
165 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
166     const bool special = _isnan(value) || !_finite(value);
167 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
168     const bool special = isnan(value) || isinf(value);
169 #else
170     const bool special = std::isnan(value) || std::isinf(value);
171 #endif
172     if (special) {
173       if (specialNumbersAllowed) {
174 #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
175         if (_isnan(value)) {
176 #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
177         if (isnan(value)) {
178 #else
179         if (std::isnan(value)) {
180 #endif
181           str += "NaN";
182         } else {
183           if (value<0) {
184             str += '-';
185           }
186           str += "Infinity";
187         }
188       } else {
189         qCritical("Attempt to write NaN or infinity, which is not supported by json");
190         error = true;
191     }
192     } else {
193       str = QByteArray::number( value , 'g', doublePrecision);
194       if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
195         str += ".0";
196       }
197     }
198   } else if ( v.type() == QVariant::Bool ) { // boolean value?
199     str = ( v.toBool() ? "true" : "false" );
200   } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
201     str = QByteArray::number( v.value<qulonglong>() );
202   } else if ( v.canConvert<qlonglong>() ) { // any signed number?
203     str = QByteArray::number( v.value<qlonglong>() );
204   } else if ( v.canConvert<QString>() ){ // can value be converted to string?
205     // this will catch QDate, QDateTime, QUrl, ...
206     str = sanitizeString( v.toString() ).toUtf8();
207     //TODO: catch other values like QImage, QRect, ...
208   } else {
209     error = true;
210   }
211   if ( !error )
212   {
213     return str;
214   }
215   else
216     return QByteArray();
217 }
218
219 QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
220 {
221    QByteArray indent;
222    if (spaces < 0) {
223      spaces = 0;
224    }
225    for (int i = 0; i < spaces; i++ ) {
226      indent += " ";
227    }
228    return indent;
229 }
230
231 QString Serializer::SerializerPrivate::sanitizeString( QString str )
232 {
233   str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
234
235   // escape unicode chars
236   QString result;
237   const ushort* unicode = str.utf16();
238   unsigned int i = 0;
239
240   while ( unicode[ i ] ) {
241     if ( unicode[ i ] < 128 ) {
242       result.append( QChar( unicode[ i ] ) );
243     }
244     else {
245       QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
246                                                            QLatin1Char('0') );
247
248       result.append( QLatin1String ("\\u") ).append( hexCode );
249     }
250     ++i;
251   }
252   str = result;
253
254   str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
255   str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
256   str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
257   str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
258   str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
259   str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
260
261   return QString( QLatin1String( "\"%1\"" ) ).arg( str );
262 }
263
264 Serializer::Serializer()
265   : d( new SerializerPrivate )
266 {
267 }
268
269 Serializer::~Serializer() {
270   delete d;
271 }
272
273 void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
274 {
275   Q_ASSERT( io );
276   if (ok)
277       *ok = false;
278
279   if (!io->isOpen()) {
280     if (!io->open(QIODevice::WriteOnly)) {
281       qCritical ("Error opening device");
282       return;
283     }
284   }
285
286   if (!io->isWritable()) {
287     qCritical ("Device is not readable");
288     io->close();
289     return;
290   }
291
292   const QByteArray str = serialize( v );
293   if (io->write(str) == str.count()) {
294     if (ok)
295       *ok = true;
296   }
297 }
298
299 QByteArray Serializer::serialize( const QVariant &v)
300 {
301   return d->serialize(v);
302 }
303
304 void QJson::Serializer::allowSpecialNumbers(bool allow) {
305   d->specialNumbersAllowed = allow;
306 }
307
308 bool QJson::Serializer::specialNumbersAllowed() const {
309   return d->specialNumbersAllowed;
310 }
311
312 void QJson::Serializer::setIndentMode(IndentMode mode) {
313   d->indentMode = mode;
314 }
315
316 void QJson::Serializer::setDoublePrecision(int precision) {
317   d->doublePrecision = precision;
318 }
319
320 IndentMode QJson::Serializer::indentMode() const {
321   return d->indentMode;
322 }