From 2ee6fbbd74767f86261d54dba6c91f2861f705cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= <morten.sorvig@digia.com>
Date: Mon, 28 Oct 2013 19:09:58 +0100
Subject: [PATCH] Add JSON parsing support to qmake.

Add qjson* implementation files from corelib/json
to the qmake build. Add a read-only compile mode,
enabled by defining QT_JSON_READONLY.

Add qmake built-in function parseJson(file, into)
which parses a json file into the given variable.

qmake uses a flat key -> value-list implementation
for storing variables, which means that some hackery
is need to represent arbitrarily nested JSON. Use a
special "_KEYS_" variable for arrays and objects:

Arrays:
["item1", "item2"]
$${array._KEYS_} -> 0 1 2
$${array.0} -> "item1"
$${array.1} -> "item2"

Objects:
{ "key1" : "value1", "key2" : "value2" }
$${object._KEYS_} -> key1 key2
$${object.key1} -> value1
$${object.key2} -> value2

Change-Id: I0aa2e4e4ae14fa25be8242bc16d3cffce32504d2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
(cherry picked from qtbase/89ef515177fd5a0b5d95dcffd5fd0b0669e3625a)
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
---
 src/shared/proparser/qmakebuiltins.cpp | 91 +++++++++++++++++++++++++-
 1 file changed, 90 insertions(+), 1 deletion(-)

diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp
index 3e30c3b85e1..36515f11320 100644
--- a/src/shared/proparser/qmakebuiltins.cpp
+++ b/src/shared/proparser/qmakebuiltins.cpp
@@ -44,6 +44,11 @@
 #include <qset.h>
 #include <qstringlist.h>
 #include <qtextstream.h>
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+# include <qjsondocument.h>
+# include <qjsonobject.h>
+# include <qjsonarray.h>
+#endif
 #ifdef PROEVALUATOR_THREAD_SAFE
 # include <qthreadpool.h>
 #endif
@@ -89,7 +94,7 @@ enum TestFunc {
     T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
     T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
     T_DEFINED, T_CONTAINS, T_INFILE,
-    T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
+    T_COUNT, T_ISEMPTY, T_PARSE_JSON, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
     T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
 };
 
@@ -166,6 +171,9 @@ void QMakeEvaluator::initFunctionStatics()
         { "infile", T_INFILE },
         { "count", T_COUNT },
         { "isEmpty", T_ISEMPTY },
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+        { "parseJson", T_PARSE_JSON },
+#endif
         { "load", T_LOAD },
         { "include", T_INCLUDE },
         { "debug", T_DEBUG },
@@ -274,6 +282,75 @@ quoteValue(const ProString &val)
     return ret;
 }
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map);
+
+static void insertJsonKeyValue(const QString &key, const QStringList &values, ProValueMap *map)
+{
+    map->insert(ProKey(key), ProStringList(values));
+}
+
+static void addJsonArray(const QJsonArray &array, const QString &keyPrefix, ProValueMap *map)
+{
+    QStringList keys;
+    for (int i = 0; i < array.count(); ++i) {
+        keys.append(QString::number(i));
+        addJsonValue(array.at(i), keyPrefix + QString::number(i), map);
+    }
+    insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), keys, map);
+}
+
+static void addJsonObject(const QJsonObject &object, const QString &keyPrefix, ProValueMap *map)
+{
+    foreach (const QString &key, object.keys())
+        addJsonValue(object.value(key), keyPrefix + key, map);
+
+    insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), object.keys(), map);
+}
+
+static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map)
+{
+    switch (value.type()) {
+    case QJsonValue::Bool:
+        insertJsonKeyValue(keyPrefix, QStringList() << (value.toBool() ? QLatin1String("true") : QLatin1String("false")), map);
+        break;
+    case QJsonValue::Double:
+        insertJsonKeyValue(keyPrefix, QStringList() << QString::number(value.toDouble()), map);
+        break;
+    case QJsonValue::String:
+        insertJsonKeyValue(keyPrefix, QStringList() << value.toString(), map);
+        break;
+    case QJsonValue::Array:
+        addJsonArray(value.toArray(), keyPrefix + QLatin1Char('.'), map);
+        break;
+    case QJsonValue::Object:
+        addJsonObject(value.toObject(), keyPrefix + QLatin1Char('.'), map);
+        break;
+    default:
+        break;
+    }
+}
+
+static QMakeEvaluator::VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value)
+{
+    QJsonDocument document = QJsonDocument::fromJson(json);
+    if (document.isNull())
+        return QMakeEvaluator::ReturnFalse;
+
+    QString currentKey = into + QLatin1Char('.');
+
+    // top-level item is either an array or object
+    if (document.isArray())
+        addJsonArray(document.array(), currentKey, value);
+    else if (document.isObject())
+        addJsonObject(document.object(), currentKey, value);
+    else
+        return QMakeEvaluator::ReturnFalse;
+
+    return QMakeEvaluator::ReturnTrue;
+}
+#endif
+
 QMakeEvaluator::VisitReturn
 QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
                           const QString &contents)
@@ -1266,6 +1343,18 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
             m_valuemapStack.top()[var] = statics.fakeValue;
         return ReturnTrue;
     }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+    case T_PARSE_JSON: {
+        if (args.count() != 2) {
+            evalError(fL1S("parseJson(variable, into) requires two arguments."));
+            return ReturnFalse;
+        }
+
+        QByteArray json = values(args.at(0).toKey()).join(QLatin1Char(' ')).toUtf8();
+        QString parseInto = args.at(1).toQString(m_tmp2);
+        return parseJsonInto(json, parseInto, &m_valuemapStack.top());
+    }
+#endif
     case T_INCLUDE: {
         if (args.count() < 1 || args.count() > 3) {
             evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments."));
-- 
GitLab