diff --git a/format.sh b/format.sh index 3fa435be1..ca3557ea7 100755 --- a/format.sh +++ b/format.sh @@ -22,7 +22,6 @@ libcockatrice_* \ exclude=("libcockatrice_rng/libcockatrice/rng/sfmt/" \ "libcockatrice_utility/libcockatrice/utility/peglib.h" \ "oracle/src/lzma/" \ -"oracle/src/qt-json/" \ "oracle/src/zip/" \ "servatrice/src/smtp/") exts=("cpp" "h" "proto") diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 0736db7f5..6a29b6935 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -23,7 +23,6 @@ set(oracle_SOURCES src/pages.cpp src/pagetemplates.cpp src/parsehelpers.cpp - src/qt-json/json.cpp ../cockatrice/src/client/settings/cache_settings.cpp ../cockatrice/src/client/settings/card_counter_settings.cpp ../cockatrice/src/client/settings/shortcuts_settings.cpp diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index fdb32bb8d..a531b82d1 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -3,9 +3,10 @@ #include "libcockatrice/interfaces/noop_card_preference_provider.h" #include "libcockatrice/interfaces/noop_card_set_priority_controller.h" #include "parsehelpers.h" -#include "qt-json/json.h" #include +#include +#include #include #include #include @@ -44,24 +45,24 @@ static CardSet::Priority getSetPriority(const QString &setType, const QString &s bool OracleImporter::readSetsFromByteArray(const QByteArray &data) { - bool ok; - auto setsMap = QtJson::Json::parse(QString(data), ok).toMap().value("data").toMap(); - if (!ok) { - qDebug() << "error: QtJson::Json::parse()"; + QJsonParseError error; + auto doc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + qDebug() << "error: QJsonDocument::fromJson():" << error.errorString(); return false; } + auto setsObj = doc.object().value("data").toObject(); + QList newSetList; - QListIterator it(setsMap.values()); - - while (it.hasNext()) { - QVariantMap map = it.next().toMap(); - QString shortName = map.value("code").toString().toUpper(); - QString longName = map.value("name").toString(); - QList setCards = map.value("cards").toList(); - QString setType = map.value("type").toString(); - QDate releaseDate = map.value("releaseDate").toDate(); + for (auto it = setsObj.constBegin(); it != setsObj.constEnd(); ++it) { + QJsonObject setObj = it.value().toObject(); + QString shortName = setObj.value("code").toString().toUpper(); + QString longName = setObj.value("name").toString(); + QJsonArray setCards = setObj.value("cards").toArray(); + QString setType = setObj.value("type").toString(); + QDate releaseDate = QDate::fromString(setObj.value("releaseDate").toString(), Qt::ISODate); CardSet::Priority priority = getSetPriority(setType, shortName); // capitalize set type if (setType.length() > 0) { @@ -142,12 +143,11 @@ CardInfoPtr OracleImporter::addCard(QString name, // Workaround for card name weirdness name = name.replace("Æ", "AE"); name = name.replace("’", "'"); - if (cards.contains(name)) { - CardInfoPtr card = cards.value(name); + auto existingIt = cards.constFind(name); + if (existingIt != cards.constEnd()) { + CardInfoPtr card = existingIt.value(); card->addToSet(printingInfo.getSet(), printingInfo); - if (card->getProperties().filter(formatRegex).empty()) { - card->combineLegalities(properties); - } + card->combineLegalities(properties); return card; } @@ -222,12 +222,12 @@ CardInfoPtr OracleImporter::addCard(QString name, return newCard; } -static QString getStringPropertyFromMap(const QVariantMap &card, const QString &propertyName) +static QString getJsonString(const QJsonObject &obj, const QString &key) { - return card.contains(propertyName) ? card.value(propertyName).toString() : QString(""); + return obj.value(key).toString(); } -int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList &cardsList) +int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QJsonArray &cardsList) { // mtgjson name => xml name static const QMap cardProperties{ @@ -248,7 +248,7 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList static const QString ptSeparator = "/"; static constexpr bool isToken = false; - static const QList setsWithCardsWithSameNameButDifferentText = {"UST"}; + static const QSet setsWithCardsWithSameNameButDifferentText = {"UST"}; int numCards = 0; @@ -256,16 +256,16 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList QMap, QString>> splitCards; // Keeps track of all names encountered so far - QList allNameProps; + QSet allNameProps; - for (const QVariant &cardVar : cardsList) { - QVariantMap card = cardVar.toMap(); + for (const QJsonValue &cardVal : cardsList) { + QJsonObject card = cardVal.toObject(); /* Currently used layouts are: * augment, double_faced_token, flip, host, leveler, meld, normal, planar, * saga, scheme, split, token, transform, vanguard */ - QString layout = getStringPropertyFromMap(card, "layout"); + QString layout = getJsonString(card, "layout"); // don't import tokens from the json file if (layout == "token") { @@ -273,9 +273,9 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList } // normal cards handling - QString name = getStringPropertyFromMap(card, "name"); - QString text = getStringPropertyFromMap(card, "text"); - QString faceName = getStringPropertyFromMap(card, "faceName"); + QString name = getJsonString(card, "name"); + QString text = getJsonString(card, "text"); + QString faceName = getJsonString(card, "faceName"); if (faceName.isEmpty()) { faceName = name; } @@ -283,39 +283,34 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList // card properties QHash properties; for (auto i = cardProperties.cbegin(), end = cardProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card, mtgjsonProperty); + QString propertyValue = getJsonString(card, i.key()); if (!propertyValue.isEmpty()) { - properties.insert(xmlPropertyName, propertyValue); + properties.insert(i.value(), propertyValue); } } // per-set properties QHash printingProps; for (auto i = setInfoProperties.cbegin(), end = setInfoProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card, mtgjsonProperty); + QString propertyValue = getJsonString(card, i.key()); if (!propertyValue.isEmpty()) { - printingProps.insert(xmlPropertyName, propertyValue); + printingProps.insert(i.value(), propertyValue); } } // handle flavorNames specially due to double-faced cards - QString faceFlavorName = getStringPropertyFromMap(card, "faceFlavorName"); - QString flavorName = !faceFlavorName.isEmpty() ? faceFlavorName : getStringPropertyFromMap(card, "flavorName"); + QString faceFlavorName = getJsonString(card, "faceFlavorName"); + QString flavorName = !faceFlavorName.isEmpty() ? faceFlavorName : getJsonString(card, "flavorName"); if (!flavorName.isEmpty()) { printingProps.insert("flavorName", flavorName); } // Identifiers + QJsonObject identifiers = card.value("identifiers").toObject(); for (auto i = identifierProperties.cbegin(), end = identifierProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card.value("identifiers").toMap(), mtgjsonProperty); + QString propertyValue = getJsonString(identifiers, i.key()); if (!propertyValue.isEmpty()) { - printingProps.insert(xmlPropertyName, propertyValue); + printingProps.insert(i.value(), propertyValue); } } @@ -331,21 +326,20 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList allNameProps.contains(faceName) && layout == "normal" && lastChar.isLetter()) { numComponent = " (" + QString(lastChar).toLower() + ")"; } - allNameProps.append(faceName); + allNameProps.insert(faceName); // special handling properties - QString colors = card.value("colors").toStringList().join(""); + QString colors = card.value("colors").toVariant().toStringList().join(""); if (!colors.isEmpty()) { properties.insert("colors", colors); } - // special handling properties - QString colorIdentity = card.value("colorIdentity").toStringList().join(""); + QString colorIdentity = card.value("colorIdentity").toVariant().toStringList().join(""); if (!colorIdentity.isEmpty()) { properties.insert("coloridentity", colorIdentity); } - const auto &mainCardType = getMainCardType(card.value("types").toStringList()); + const auto &mainCardType = getMainCardType(card.value("types").toVariant().toStringList()); if (mainCardType.isEmpty()) { qDebug() << "warning: no mainCardType for card:" << name; } else { @@ -354,22 +348,22 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList // Depending on whether power and/or toughness are present, the format // is either P/T (most common), P (no toughness), or /T (no power). - QString power = getStringPropertyFromMap(card, "power"); - QString toughness = getStringPropertyFromMap(card, "toughness"); + QString power = getJsonString(card, "power"); + QString toughness = getJsonString(card, "toughness"); if (toughness.isEmpty() && !power.isEmpty()) { properties.insert("pt", power); } else if (!toughness.isEmpty()) { properties.insert("pt", power + ptSeparator + toughness); } - auto legalities = card.value("legalities").toMap(); - for (auto i = legalities.cbegin(), end = legalities.cend(); i != end; ++i) { + auto legalities = card.value("legalities").toObject(); + for (auto i = legalities.constBegin(), end = legalities.constEnd(); i != end; ++i) { properties.insert(QString("format-%1").arg(i.key()), i.value().toString().toLower()); } // split cards are considered a single card, enqueue for later merging if (layout == "split" || layout == "aftermath" || layout == "adventure" || layout == "prepare") { - auto _faceName = getStringPropertyFromMap(card, "faceName"); + auto _faceName = getJsonString(card, "faceName"); SplitCardPart split(_faceName, text, properties, printingInfo); auto found_iter = splitCards.find(name + numProperty); if (found_iter == splitCards.end()) { @@ -382,11 +376,11 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList QList relatedCards; // add other face for split cards as card relation - if (!getStringPropertyFromMap(card, "side").isEmpty()) { - auto faceManaValue = getStringPropertyFromMap(card, "faceManaValue"); + if (!getJsonString(card, "side").isEmpty()) { + auto faceManaValue = getJsonString(card, "faceManaValue"); if (faceManaValue.isEmpty()) { // check the old name for the property, for backwards compatibility purposes - faceManaValue = getStringPropertyFromMap(card, "faceConvertedManaCost"); + faceManaValue = getJsonString(card, "faceConvertedManaCost"); } properties["cmc"] = faceManaValue; @@ -406,15 +400,15 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList name = faceName; } - // mtgjon related cards - if (card.contains("relatedCards")) { - QVariantMap givenRelated = card.value("relatedCards").toMap(); + // mtgjson related cards + QJsonObject givenRelated = card.value("relatedCards").toObject(); + if (!givenRelated.isEmpty()) { // conjured cards from a spellbook - if (givenRelated.contains("spellbook")) { - auto spbk = givenRelated.value("spellbook").toStringList(); - for (const QString &spbkName : spbk) { - relatedCards.append( - new CardRelation(spbkName, CardRelationType::DoesNotAttach, false, false, 1, true)); + QJsonArray spellbook = givenRelated.value("spellbook").toArray(); + if (!spellbook.isEmpty()) { + for (const QJsonValue &spbkVal : spellbook) { + relatedCards.append(new CardRelation(spbkVal.toString(), CardRelationType::DoesNotAttach, false, + false, 1, true)); } } } @@ -427,7 +421,6 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList // split cards handling static const QString splitCardPropSeparator = QString(" // "); static const QString splitCardTextSeparator = QString("\n\n---\n\n"); - static const QList noRelatedCards = {}; QList, QString>> partsAndNames = splitCards.values(); for (auto [splitCardParts, name] : partsAndNames) { @@ -465,20 +458,20 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList } } } - CardInfoPtr newCard = addCard(name, text, isToken, properties, noRelatedCards, printingInfo); + CardInfoPtr newCard = addCard(name, text, isToken, properties, {}, printingInfo); numCards++; } return numCards; } -FormatRulesNameMap OracleImporter::createDefaultMagicFormats() +static FormatRulesNameMap buildDefaultMagicFormats() { // Predefined common exceptions CardCondition superTypeIsBasic; superTypeIsBasic.field = "type"; superTypeIsBasic.matchType = "regex"; - superTypeIsBasic.value = "\bBasic\b[^—]+\bLand\b"; + superTypeIsBasic.value = R"(\bBasic\b[^—]+\bLand\b)"; ExceptionRule basicLands; basicLands.conditions.append(superTypeIsBasic); @@ -491,7 +484,6 @@ FormatRulesNameMap OracleImporter::createDefaultMagicFormats() ExceptionRule mayContainAnyNumber; mayContainAnyNumber.conditions.append(anyNumberAllowed); - // Map to store default rules FormatRulesNameMap defaultFormatRulesNameMap; // ----------------- Helper lambda to create format ----------------- @@ -537,6 +529,12 @@ FormatRulesNameMap OracleImporter::createDefaultMagicFormats() return defaultFormatRulesNameMap; } +FormatRulesNameMap OracleImporter::createDefaultMagicFormats() +{ + static const FormatRulesNameMap cached = buildDefaultMagicFormats(); + return cached; +} + int OracleImporter::startImport() { static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController(); @@ -576,6 +574,11 @@ bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUr return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName, sourceUrl, sourceVersion); } +void OracleImporter::releaseSetData() +{ + allSets.clear(); +} + void OracleImporter::clear() { sets.clear(); diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 99644f9ce..696e35cab 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -1,6 +1,8 @@ #ifndef ORACLEIMPORTER_H #define ORACLEIMPORTER_H +#include +#include #include #include #include @@ -44,7 +46,7 @@ class SetToDownload { private: QString shortName, longName; - QList cards; + QJsonArray cards; QDate releaseDate; QString setType; CardSet::Priority priority; @@ -58,7 +60,7 @@ public: { return longName; } - const QList &getCards() const + const QJsonArray &getCards() const { return cards; } @@ -76,7 +78,7 @@ public: } SetToDownload(QString _shortName, QString _longName, - QList _cards, + QJsonArray _cards, CardSet::Priority _priority, QString _setType = QString(), const QDate &_releaseDate = QDate()) @@ -154,7 +156,7 @@ public: bool readSetsFromByteArray(const QByteArray &data); int startImport(); bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion); - int importCardsFromSet(const CardSetPtr ¤tSet, const QList &cardsList); + int importCardsFromSet(const CardSetPtr ¤tSet, const QJsonArray &cardsList); FormatRulesNameMap createDefaultMagicFormats(); const CardNameMap &getCardList() const { @@ -164,6 +166,7 @@ public: { return allSets; } + void releaseSetData(); void clear(); }; diff --git a/oracle/src/pages.cpp b/oracle/src/pages.cpp index df4d1a98c..94e662ffe 100644 --- a/oracle/src/pages.cpp +++ b/oracle/src/pages.cpp @@ -560,6 +560,9 @@ void SaveSetsPage::initializePage() int setsImported = wizard()->importer->startImport(); + // JSON data no longer needed after CardInfo objects are built + wizard()->importer->releaseSetData(); + if (setsImported == 0) { QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); } diff --git a/oracle/src/qt-json/AUTHORS b/oracle/src/qt-json/AUTHORS deleted file mode 100644 index 29a85929f..000000000 --- a/oracle/src/qt-json/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -Eeli Reilin -Luis Gustavo S. Barreto -Stephen Kockentiedt diff --git a/oracle/src/qt-json/LICENSE b/oracle/src/qt-json/LICENSE deleted file mode 100644 index 3c42b515a..000000000 --- a/oracle/src/qt-json/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2011 Eeli Reilin. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of Eeli Reilin. - diff --git a/oracle/src/qt-json/README b/oracle/src/qt-json/README deleted file mode 100644 index b60c1599b..000000000 --- a/oracle/src/qt-json/README +++ /dev/null @@ -1,96 +0,0 @@ -######################################################################## -1. INTRODUCTION - -The Json class is a simple class for parsing JSON data into a QVariant -hierarchies. Now, we can also reverse the process and serialize -QVariant hierarchies into valid JSON data. - - -######################################################################## -2. HOW TO USE - -The parser is really easy to use. Let's say we have the following -QString of JSON data: - ------------------------------------------------------------------------- -{ - "encoding" : "UTF-8", - "plug-ins" : [ - "python", - "c++", - "ruby" - ], - "indent" : { - "length" : 3, - "use_space" : true - } -} ------------------------------------------------------------------------- - -We would first call the parse-method: - ------------------------------------------------------------------------- -//Say that we're using the QtJson namespace -using namespace QtJson; -bool ok; -//json is a QString containing the JSON data -QVariantMap result = Json::parse(json, ok).toMap(); - -if(!ok) { - qFatal("An error occurred during parsing"); - exit(1); -} ------------------------------------------------------------------------- - -Assuming the parsing process completed without errors, we would then -go through the hierarchy: - ------------------------------------------------------------------------- -qDebug() << "encoding:" << result["encoding"].toString(); -qDebug() << "plugins:"; - -foreach(QVariant plugin, result["plug-ins"].toList()) { - qDebug() << "\t-" << plugin.toString(); -} - -QVariantMap nestedMap = result["indent"].toMap(); -qDebug() << "length:" << nestedMap["length"].toInt(); -qDebug() << "use_space:" << nestedMap["use_space"].toBool(); ------------------------------------------------------------------------- - -The previous code would print out the following: - ------------------------------------------------------------------------- -encoding: "UTF-8" -plugins: - - "python" - - "c++" - - "ruby" -length: 3 -use_space: true ------------------------------------------------------------------------- - -To write JSON data from Qt object is as simple as parsing: - ------------------------------------------------------------------------- -QVariantMap map; -map["name"] = "Name"; -map["age"] = 22; - -QByteArray data = Json::serialize(map); ------------------------------------------------------------------------- - -The byte array 'data' contains valid JSON data: - ------------------------------------------------------------------------- -{ - name: "Luis Gustavo", - age: 22, -} ------------------------------------------------------------------------- - - -######################################################################## -4. CONTRIBUTING - -The code is available to download at GitHub. Contribute if you dare! diff --git a/oracle/src/qt-json/json.cpp b/oracle/src/qt-json/json.cpp deleted file mode 100644 index ff739b49d..000000000 --- a/oracle/src/qt-json/json.cpp +++ /dev/null @@ -1,545 +0,0 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.cpp - */ - -#include "json.h" - -#include -#include - -namespace QtJson -{ - -static QString sanitizeString(QString str) -{ - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); -} - -static QByteArray join(const QList &list, const QByteArray &sep) -{ - QByteArray res; - for (const QByteArray &i : list) { - if (!res.isEmpty()) { - res += sep; - } - res += i; - } - return res; -} - -/** - * parse - */ -QVariant Json::parse(const QString &json) -{ - bool success = true; - return Json::parse(json, success); -} - -/** - * parse - */ -QVariant Json::parse(const QString &json, bool &success) -{ - success = true; - - // Return an empty QVariant if the JSON data is either null or empty - if (!json.isNull() || !json.isEmpty()) { - // We'll start from index 0 - int index = 0; - - // Parse the first value - QVariant value = Json::parseValue(json, index, success); - - // Return the parsed value - return value; - } else { - // Return the empty QVariant - return QVariant(); - } -} - -QByteArray Json::serialize(const QVariant &data) -{ - bool success = true; - return Json::serialize(data, success); -} - -QByteArray Json::serialize(const QVariant &data, bool &success) -{ - QByteArray str; - success = true; - - if (!data.isValid()) // invalid or null? - { - str = "null"; - } - else if ((data.typeId() == QMetaType::Type::QVariantList) || - (data.typeId() == QMetaType::Type::QStringList)) // variant is a list? - { - QList values; - const QVariantList list = data.toList(); - for (const QVariant &v : list) { - QByteArray serializedValue = serialize(v); - if (serializedValue.isNull()) { - success = false; - break; - } - values << serializedValue; - } - - str = "[ " + join(values, ", ") + " ]"; - } - else if ((data.typeId() == QMetaType::Type::QVariantHash)) // variant is a hash? - { - const QVariantHash vhash = data.toHash(); - QHashIterator it(vhash); - str = "{ "; - QList pairs; - - while (it.hasNext()) { - it.next(); - QByteArray serializedValue = serialize(it.value()); - - if (serializedValue.isNull()) { - success = false; - break; - } - - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - - str += join(pairs, ", "); - str += " }"; - } - else if ((data.typeId() == QMetaType::Type::QVariantMap)) // variant is a map? - { - const QVariantMap vmap = data.toMap(); - QMapIterator it(vmap); - str = "{ "; - QList pairs; - while (it.hasNext()) { - it.next(); - QByteArray serializedValue = serialize(it.value()); - if (serializedValue.isNull()) { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - str += join(pairs, ", "); - str += " }"; - } - else if ((data.typeId() == QMetaType::Type::QString) || - (data.typeId() == QMetaType::Type::QByteArray)) // a string or a byte array? - { - str = sanitizeString(data.toString()).toUtf8(); - } - else if (data.typeId() == QMetaType::Type::Double) // double? - { - str = QByteArray::number(data.toDouble(), 'g', 20); - if (!str.contains(".") && !str.contains("e")) { - str += ".0"; - } - } - else if (data.typeId() == QMetaType::Type::Bool) // boolean value? - { - str = data.toBool() ? "true" : "false"; - } - else if (data.typeId() == QMetaType::Type::ULongLong) // large unsigned number? - { - str = QByteArray::number(data.value()); - } else if (data.canConvert()) // any signed number? - { - str = QByteArray::number(data.value()); - } else if (data.canConvert()) { - str = QString::number(data.value()).toUtf8(); - } else if (data.canConvert()) // can value be converted to string? - { - // this will catch QDate, QDateTime, QUrl, ... - str = sanitizeString(data.toString()).toUtf8(); - } else { - success = false; - } - if (success) { - return str; - } else { - return QByteArray(); - } -} - -/** - * parseValue - */ -QVariant Json::parseValue(const QString &json, int &index, bool &success) -{ - // Determine what kind of data we should parse by - // checking out the upcoming token - switch (Json::lookAhead(json, index)) { - case JsonTokenString: - return Json::parseString(json, index, success); - case JsonTokenNumber: - return Json::parseNumber(json, index); - case JsonTokenCurlyOpen: - return Json::parseObject(json, index, success); - case JsonTokenSquaredOpen: - return Json::parseArray(json, index, success); - case JsonTokenTrue: - Json::nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - Json::nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - Json::nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } - - // If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); -} - -/** - * parseObject - */ -QVariant Json::parseObject(const QString &json, int &index, bool &success) -{ - QVariantMap map; - int token; - - // Get rid of the whitespace and increment index - Json::nextToken(json, index); - - // Loop through all of the key/value pairs of the object - bool done = false; - while (!done) { - // Get the upcoming token - token = Json::lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantMap(); - } else if (token == JsonTokenComma) { - Json::nextToken(json, index); - } else if (token == JsonTokenCurlyClose) { - Json::nextToken(json, index); - return map; - } else { - // Parse the key/value pair's name - QString name = Json::parseString(json, index, success).toString(); - - if (!success) { - return QVariantMap(); - } - - // Get the next token - token = Json::nextToken(json, index); - - // If the next token is not a colon, flag the failure - // return an empty QVariant - if (token != JsonTokenColon) { - success = false; - return QVariant(QVariantMap()); - } - - // Parse the key/value pair's value - QVariant value = Json::parseValue(json, index, success); - - if (!success) { - return QVariantMap(); - } - - // Assign the value to the key in the map - map[name] = value; - } - } - - // Return the map successfully - return QVariant(map); -} - -/** - * parseArray - */ -QVariant Json::parseArray(const QString &json, int &index, bool &success) -{ - QVariantList list; - - Json::nextToken(json, index); - - bool done = false; - while (!done) { - int token = Json::lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantList(); - } else if (token == JsonTokenComma) { - Json::nextToken(json, index); - } else if (token == JsonTokenSquaredClose) { - Json::nextToken(json, index); - break; - } else { - QVariant value = Json::parseValue(json, index, success); - - if (!success) { - return QVariantList(); - } - - list.push_back(value); - } - } - - return QVariant(list); -} - -/** - * parseString - */ -QVariant Json::parseString(const QString &json, int &index, bool &success) -{ - QString s; - QChar c; - - Json::eatWhitespace(json, index); - - c = json[index++]; - - bool complete = false; - while (!complete) { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - complete = true; - break; - } else if (c == '\\') { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - s.append('\"'); - } else if (c == '\\') { - s.append('\\'); - } else if (c == '/') { - s.append('/'); - } else if (c == 'b') { - s.append('\b'); - } else if (c == 'f') { - s.append('\f'); - } else if (c == 'n') { - s.append('\n'); - } else if (c == 'r') { - s.append('\r'); - } else if (c == 't') { - s.append('\t'); - } else if (c == 'u') { - int remainingLength = json.size() - index; - - if (remainingLength >= 4) { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } else { - break; - } - } - } else { - s.append(c); - } - } - - if (!complete) { - success = false; - return QVariant(); - } - - return QVariant(s); -} - -/** - * parseNumber - */ -QVariant Json::parseNumber(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - int lastIndex = Json::lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; - - numberStr = json.mid(index, charLength); - - index = lastIndex + 1; - - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - return QVariant(numberStr.toLongLong(NULL)); - } else { - return QVariant(numberStr.toULongLong(NULL)); - } -} - -/** - * lastIndexOfNumber - */ -int Json::lastIndexOfNumber(const QString &json, int index) -{ - static const QString numericCharacters("0123456789+-.eE"); - int lastIndex; - - for (lastIndex = index; lastIndex < json.size(); lastIndex++) { - if (numericCharacters.indexOf(json[lastIndex]) == -1) { - break; - } - } - - return lastIndex - 1; -} - -/** - * eatWhitespace - */ -void Json::eatWhitespace(const QString &json, int &index) -{ - static const QString whitespaceChars(" \t\n\r"); - for (; index < json.size(); index++) { - if (whitespaceChars.indexOf(json[index]) == -1) { - break; - } - } -} - -/** - * lookAhead - */ -int Json::lookAhead(const QString &json, int index) -{ - int saveIndex = index; - return Json::nextToken(json, saveIndex); -} - -/** - * nextToken - */ -int Json::nextToken(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - if (index == json.size()) { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch (c.toLatin1()) { - case '{': - return JsonTokenCurlyOpen; - case '}': - return JsonTokenCurlyClose; - case '[': - return JsonTokenSquaredOpen; - case ']': - return JsonTokenSquaredClose; - case ',': - return JsonTokenComma; - case '"': - return JsonTokenString; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return JsonTokenNumber; - case ':': - return JsonTokenColon; - } - - index--; - - int remainingLength = json.size() - index; - - // True - if (remainingLength >= 4) { - if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { - index += 4; - return JsonTokenTrue; - } - } - - // False - if (remainingLength >= 5) { - if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') { - index += 5; - return JsonTokenFalse; - } - } - - // Null - if (remainingLength >= 4) { - if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { - index += 4; - return JsonTokenNull; - } - } - - return JsonTokenNone; -} - -} // namespace QtJson diff --git a/oracle/src/qt-json/json.h b/oracle/src/qt-json/json.h deleted file mode 100644 index cf0499d4e..000000000 --- a/oracle/src/qt-json/json.h +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.h - */ - -#ifndef JSON_H -#define JSON_H - -#include -#include - -namespace QtJson -{ - -/** - * \enum JsonToken - */ -enum JsonToken -{ - JsonTokenNone = 0, - JsonTokenCurlyOpen = 1, - JsonTokenCurlyClose = 2, - JsonTokenSquaredOpen = 3, - JsonTokenSquaredClose = 4, - JsonTokenColon = 5, - JsonTokenComma = 6, - JsonTokenString = 7, - JsonTokenNumber = 8, - JsonTokenTrue = 9, - JsonTokenFalse = 10, - JsonTokenNull = 11 -}; - -/** - * \class Json - * \brief A JSON data parser - * - * Json parses a JSON data into a QVariant hierarchy. - */ -class Json -{ - public: - /** - * Parse a JSON string - * - * \param json The JSON data - */ - static QVariant parse(const QString &json); - - /** - * Parse a JSON string - * - * \param json The JSON data - * \param success The success of the parsing - */ - static QVariant parse(const QString &json, bool &success); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - */ - static QByteArray serialize(const QVariant &data); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - * - * \return QByteArray Textual JSON representation - */ - static QByteArray serialize(const QVariant &data, bool &success); - - private: - /** - * Parses a value starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the parse process - * - * \return QVariant The parsed value - */ - static QVariant parseValue(const QString &json, int &index, - bool &success); - - /** - * Parses an object starting from index - * - * \param json The JSON data - * \param index The start index - * \param success The success of the object parse - * - * \return QVariant The parsed object map - */ - static QVariant parseObject(const QString &json, int &index, - bool &success); - - /** - * Parses an array starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the array parse - * - * \return QVariant The parsed variant array - */ - static QVariant parseArray(const QString &json, int &index, - bool &success); - - /** - * Parses a string starting from index - * - * \param json The JSON data - * \param index The starting index - * \param success The success of the string parse - * - * \return QVariant The parsed string - */ - static QVariant parseString(const QString &json, int &index, - bool &success); - - /** - * Parses a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return QVariant The parsed number - */ - static QVariant parseNumber(const QString &json, int &index); - - /** - * Get the last index of a number starting from index - * - * \param json The JSON data - * \param index The starting index - * - * \return The last index of the number - */ - static int lastIndexOfNumber(const QString &json, int index); - - /** - * Skip unwanted whitespace symbols starting from index - * - * \param json The JSON data - * \param index The start index - */ - static void eatWhitespace(const QString &json, int &index); - - /** - * Check what token lies ahead - * - * \param json The JSON data - * \param index The starting index - * - * \return int The upcoming token - */ - static int lookAhead(const QString &json, int index); - - /** - * Get the next JSON token - * - * \param json The JSON data - * \param index The starting index - * - * \return int The next JSON token - */ - static int nextToken(const QString &json, int &index); -}; - - -} //end namespace - -#endif //JSON_H