TTKMusicPlayer  3.7.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
serializer.cpp
Go to the documentation of this file.
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  * Copyright (C) 2016 Anton Kudryavtsev <a.kudryavtsev@netris.ru>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License version 2.1, as published by the Free Software Foundation.
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/QIODevice>
25 #include <QtCore/QDataStream>
26 #include <QtCore/QStringList>
27 #include <QtCore/QVariant>
28 
29 // cmath does #undef for isnan and isinf macroses what can be defined in math.h
30 #if defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
31 # include <math.h>
32 #else
33 # include <cmath>
34 #endif
35 
36 #ifdef Q_OS_SOLARIS
37 # ifndef isinf
38 # include <ieeefp.h>
39 # define isinf(x) (!finite((x)) && (x)==(x))
40 # endif
41 #endif
42 
43 #ifdef _MSC_VER // using MSVC compiler
44 #include <float.h>
45 #endif
46 
47 using namespace QJson;
48 
52 class Serializer::SerializerPrivate : public TTKPrivate<Serializer>
53 {
54  public:
56  specialNumbersAllowed(false),
58  doublePrecision(6) {
59  errorMessage.clear();
60  }
61  QString errorMessage;
65 
66  QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
67 
68  static QByteArray buildIndent(int spaces);
69  static QByteArray escapeString(const QString& str);
70  static QByteArray join(const QList<QByteArray>& list, const QByteArray &sep);
71  static QByteArray join(const QList<QByteArray>& list, char sep);
72 };
73 
74 QByteArray Serializer::SerializerPrivate::join(const QList<QByteArray>& list, const QByteArray &sep)
75 {
76  QByteArray res;
77  for( const QByteArray &i : qAsConst(list) ) {
78  if ( !res.isEmpty() )
79  res += sep;
80  res += i;
81  }
82  return res;
83 }
84 
85 QByteArray Serializer::SerializerPrivate::join(const QList<QByteArray>& list, char sep)
86 {
87  QByteArray res;
88  for( const QByteArray &i : qAsConst(list) ) {
89  if ( !res.isEmpty() )
90  res += sep;
91  res += i;
92  }
93  return res;
94 }
95 
96 QByteArray Serializer::SerializerPrivate::serialize(const QVariant &v, bool *ok, int indentLevel)
97 {
98  QByteArray str;
99 #if TTK_QT_VERSION_CHECK(6,0,0)
100  const int type = v.metaType().id();
101 #else
102  const int type = v.type();
103 #endif
104 
105  if ( ! v.isValid() ) { // invalid or null?
106  str = "null";
107  }
108  else if (( type == QMetaType::QVariantList ) || ( type == QMetaType::QStringList ))
109  { // an array or a stringlist?
110  const QVariantList list = v.toList();
111  QList<QByteArray> values;
112  for( const QVariant &var : qAsConst(list) )
113  {
114  QByteArray serializedValue;
115 
116  serializedValue = serialize( var, ok, indentLevel+1);
117 
118  if ( !*ok ) {
119  break;
120  }
121  switch(indentMode) {
122  case QJson::IndentFull :
123  case QJson::IndentMedium :
124  case QJson::IndentMinimum :
125  values << serializedValue;
126  break;
127  case QJson::IndentCompact :
128  case QJson::IndentNone :
129  default:
130  values << serializedValue.trimmed();
131  break;
132  }
133  }
134 
135  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull ) {
136  QByteArray indent = buildIndent(indentLevel);
137  str = indent + "[\n" + join( values, ",\n" ) + '\n' + indent + ']';
138  }
139  else if (indentMode == QJson::IndentMinimum) {
140  QByteArray indent = buildIndent(indentLevel);
141  str = indent + "[\n" + join( values, ",\n" ) + '\n' + indent + ']';
142  }
143  else if (indentMode == QJson::IndentCompact) {
144  str = '[' + join( values, "," ) + ']';
145  }
146  else {
147  str = "[ " + join( values, ", " ) + " ]";
148  }
149  }
150  else if ( type == QMetaType::QVariantMap )
151  { // variant is a map?
152  const QVariantMap vmap = v.toMap();
153 
154  if (indentMode == QJson::IndentMinimum) {
155  QByteArray indent = buildIndent(indentLevel);
156  str = indent + "{ ";
157  }
158  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
159  QByteArray indent = buildIndent(indentLevel);
160  QByteArray nextindent = buildIndent(indentLevel + 1);
161  str = indent + "{\n" + nextindent;
162  }
163  else if (indentMode == QJson::IndentCompact) {
164  str = "{";
165  }
166  else {
167  str = "{ ";
168  }
169 
170  QList<QByteArray> pairs;
171  for (QVariantMap::const_iterator it = vmap.begin(), end = vmap.end(); it != end; ++it) {
172  indentLevel++;
173  QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
174  indentLevel--;
175  if ( !*ok ) {
176  break;
177  }
178  QByteArray key = escapeString( it.key() );
179  QByteArray value = serializedValue.trimmed();
180  if (indentMode == QJson::IndentCompact) {
181  pairs << key + ':' + value;
182  } else {
183  pairs << key + " : " + value;
184  }
185  }
186 
187  if (indentMode == QJson::IndentFull) {
188  QByteArray indent = buildIndent(indentLevel + 1);
189  str += join( pairs, ",\n" + indent);
190  }
191  else if (indentMode == QJson::IndentCompact) {
192  str += join( pairs, ',' );
193  }
194  else {
195  str += join( pairs, ", " );
196  }
197 
198  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
199  QByteArray indent = buildIndent(indentLevel);
200  str += '\n' + indent + '}';
201  }
202  else if (indentMode == QJson::IndentCompact) {
203  str += '}';
204  }
205  else {
206  str += " }";
207  }
208  }
209  else if ( type == QMetaType::QVariantHash )
210  { // variant is a hash?
211  const QVariantHash vhash = v.toHash();
212 
213  if (indentMode == QJson::IndentMinimum) {
214  QByteArray indent = buildIndent(indentLevel);
215  str = indent + "{ ";
216  }
217  else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
218  QByteArray indent = buildIndent(indentLevel);
219  QByteArray nextindent = buildIndent(indentLevel + 1);
220  str = indent + "{\n" + nextindent;
221  }
222  else if (indentMode == QJson::IndentCompact) {
223  str = "{";
224  }
225  else {
226  str = "{ ";
227  }
228 
229  QList<QByteArray> pairs;
230  for (QVariantHash::const_iterator it = vhash.begin(), end = vhash.end(); it != end; ++it) {
231  QByteArray serializedValue = serialize( it.value(), ok, indentLevel + 1);
232 
233  if ( !*ok ) {
234  break;
235  }
236  QByteArray key = escapeString( it.key() );
237  QByteArray value = serializedValue.trimmed();
238  if (indentMode == QJson::IndentCompact) {
239  pairs << key + ':' + value;
240  } else {
241  pairs << key + " : " + value;
242  }
243  }
244 
245  if (indentMode == QJson::IndentFull) {
246  QByteArray indent = buildIndent(indentLevel + 1);
247  str += join( pairs, ",\n" + indent);
248  }
249  else if (indentMode == QJson::IndentCompact) {
250  str += join( pairs, ',' );
251  }
252  else {
253  str += join( pairs, ", " );
254  }
255 
256  if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
257  QByteArray indent = buildIndent(indentLevel);
258  str += '\n' + indent + '}';
259  }
260  else if (indentMode == QJson::IndentCompact) {
261  str += '}';
262  }
263  else {
264  str += " }";
265  }
266 
267  } else {
268  // Add indent, we may need to remove it later for some layouts
269  switch(indentMode) {
270  case QJson::IndentFull :
271  case QJson::IndentMedium :
272  case QJson::IndentMinimum :
273  str += buildIndent(indentLevel);
274  break;
275  case QJson::IndentCompact :
276  case QJson::IndentNone :
277  default:
278  break;
279  }
280 
281  if (( type == QMetaType::QString ) || ( type == QMetaType::QByteArray ))
282  { // a string or a byte array?
283  str += escapeString( v.toString() );
284  }
285  else if (( type == QMetaType::Double) || (type == QMetaType::Float))
286  { // a double or a float?
287  const double value = v.toDouble();
288 // #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
290 // const bool special = std::isnan(value) || std::isinf(value);
291 // #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
292 // const bool special = isnan(value) || isinf(value);
293 // #else
294  const bool special = std::isnan(value) || std::isinf(value);
295 // #endif
296  if (special) {
297  if (specialNumbersAllowed) {
298  #if defined _WIN32 && !defined(Q_OS_SYMBIAN)
299 // if (_isnan(value)) {
300  if (std::isnan(value)) {
301  #elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_SOLARIS)
302  if (isnan(value)) {
303  #else
304  if (std::isnan(value)) {
305  #endif
306  str += "NaN";
307  } else {
308  if (value<0) {
309  str += '-';
310  }
311  str += "Infinity";
312  }
313  } else {
314  errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
315  *ok = false;
316  }
317  } else {
318  str = QByteArray::number( value , 'g', doublePrecision);
319  if( !str.contains( '.' ) && !str.contains( 'e' ) ) {
320  str += ".0";
321  }
322  }
323  }
324  else if (( type == QMetaType::Bool))
325  { // boolean value?
326  str += ( v.toBool() ? "true" : "false" );
327  }
328  else if (( type == QMetaType::ULongLong))
329  { // large unsigned number?
330  str += QByteArray::number( v.value<qulonglong>() );
331  }
332  else if (( type == QMetaType::UInt))
333  { // unsigned int number?
334  str += QByteArray::number( v.value<quint32>() );
335  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
336  str += QByteArray::number( v.value<qlonglong>() );
337  } else if ( v.canConvert<int>() ) { // unsigned short number?
338  str += QByteArray::number( v.value<int>() );
339  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
340  // this will catch QDate, QDateTime, QUrl, ...
341  str += escapeString( v.toString() );
342  //TODO: catch other values like QImage, QRect, ...
343  } else {
344  *ok = false;
345  errorMessage += QLatin1String("Cannot serialize ");
346  errorMessage += v.toString();
347  errorMessage += QLatin1String(" because type ");
348  errorMessage += QLatin1String(v.typeName());
349  errorMessage += QLatin1String(" is not supported by QJson\n");
350  }
351  }
352  if ( *ok )
353  {
354  return str;
355  }
356  else
357  return QByteArray();
358 }
359 
361 {
362  QByteArray indent;
363  if (spaces < 0) {
364  spaces = 0;
365  }
366  for (int i = 0; i < spaces; i++ ) {
367  indent += ' ';
368  }
369  return indent;
370 }
371 
372 QByteArray Serializer::SerializerPrivate::escapeString(const QString& str)
373 {
374  QByteArray result;
375  result.reserve(str.size() + 2);
376  result.append('\"');
377  for (QString::const_iterator it = str.begin(), end = str.end(); it != end; ++it) {
378  ushort unicode = it->unicode();
379  switch ( unicode ) {
380  case '\"':
381  result.append("\\\"");
382  break;
383  case '\\':
384  result.append("\\\\");
385  break;
386  case '\b':
387  result.append("\\b");
388  break;
389  case '\f':
390  result.append("\\f");
391  break;
392  case '\n':
393  result.append("\\n");
394  break;
395  case '\r':
396  result.append("\\r");
397  break;
398  case '\t':
399  result.append("\\t");
400  break;
401  default:
402  if ( unicode > 0x1F && unicode < 128 ) {
403  result.append(TTKStaticCast(char, unicode));
404  } else {
405  char escaped[7];
406  qsnprintf(escaped, sizeof(escaped)/sizeof(char), "\\u%04x", unicode);
407  result.append(escaped);
408  }
409  }
410  }
411  result.append('\"');
412  return result;
413 }
414 
415 Serializer::Serializer()
416 {
418 }
419 
420 void Serializer::serialize(const QVariant &v, QIODevice* io, bool* ok)
421 {
422  Q_ASSERT( io );
423  TTK_D(Serializer);
424 
425  *ok = true;
426 
427  if (!io->isOpen()) {
428  if (!io->open(QIODevice::WriteOnly)) {
429  d->errorMessage = QLatin1String("Error opening device");
430  *ok = false;
431  return;
432  }
433  }
434 
435  if (!io->isWritable()) {
436  d->errorMessage = QLatin1String("Device is not readable");
437  io->close();
438  *ok = false;
439  return;
440  }
441 
442  const QByteArray str = serialize( v, ok);
443  if (*ok && (io->write(str) != str.length())) {
444  *ok = false;
445  d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
446  }
447 }
448 
449 QByteArray Serializer::serialize(const QVariant &v, bool *ok)
450 {
451  TTK_D(Serializer);
452  bool _ok = true;
453  d->errorMessage.clear();
454 
455  if (ok) {
456  *ok = true;
457  } else {
458  ok = &_ok;
459  }
460 
461  return d->serialize(v, ok);
462 }
463 
465 {
466  TTK_D(Serializer);
467  d->specialNumbersAllowed = allow;
468 }
469 
471 {
472  TTK_D(Serializer);
473  return d->specialNumbersAllowed;
474 }
475 
477 {
478  TTK_D(Serializer);
479  d->indentMode = mode;
480 }
481 
483 {
484  TTK_D(Serializer);
485  d->doublePrecision = precision;
486 }
487 
489 {
490  TTK_D(Serializer);
491  return d->indentMode;
492 }
493 
495 {
496  TTK_D(Serializer);
497  return d->errorMessage;
498 }
bool specialNumbersAllowed() const
Is Nan and/or Infinity allowed?
Definition: serializer.cpp:470
#define TTKStaticCast(x, y)
Definition: ttkglobal.h:159
static QByteArray buildIndent(int spaces)
Definition: serializer.cpp:360
static constexpr wchar_t key[]
IndentMode indentMode() const
Returns one of the indentation modes defined in QJson::IndentMode.
Definition: serializer.cpp:488
Main class used to convert QVariant objects to JSON data.
Definition: serializer.h:151
#define qAsConst
Definition: ttkqtglobal.h:53
IndentMode
How the indentation should work.
Definition: serializer.h:103
void setIndentMode(IndentMode mode=QJson::IndentNone)
set output indentation mode as defined in QJson::IndentMode
Definition: serializer.cpp:476
Namespace used by QJson.
Definition: parser.h:34
QByteArray serialize(const QVariant &v, bool *ok, int indentLevel=0)
Definition: serializer.cpp:96
static QByteArray join(const QList< QByteArray > &list, const QByteArray &sep)
Definition: serializer.cpp:74
void setDoublePrecision(int precision)
set double precision used while converting Double
Definition: serializer.cpp:482
void allowSpecialNumbers(bool allow)
Allow or disallow writing of NaN and/or Infinity (as an extension to QJson)
Definition: serializer.cpp:464
static QByteArray escapeString(const QString &str)
Definition: serializer.cpp:372
QString errorMessage() const
Returns the error message.
Definition: serializer.cpp:494
const char int mode
Definition: ioapi.h:135
#define TTK_INIT_PRIVATE(Class)
Definition: ttkprivate.h:33
The class of the ttk private base.
Definition: ttkprivate.h:48
Main class used to convert QVariant objects to JSON data private.
Definition: serializer.cpp:52
#define TTK_D(Class)
Definition: ttkprivate.h:41