File indexing completed on 2024-06-16 03:42:49

0001 /*
0002     SPDX-FileCopyrightText: 2011 SCHUTZ Sacha
0003     SPDX-FileCopyrightText: 2020-2023 Alexander Semke <alexander.semke@web.de>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 #include "QJsonModel.h"
0009 #include "backend/lib/trace.h"
0010 
0011 #include <QFile>
0012 #include <QPainter>
0013 #include <QPalette>
0014 
0015 #include <KLocalizedString>
0016 
0017 QJsonTreeItem::QJsonTreeItem(QJsonTreeItem* parent)
0018     : mParent(parent) {
0019 }
0020 
0021 QJsonTreeItem::~QJsonTreeItem() {
0022     qDeleteAll(mChildren);
0023 }
0024 
0025 void QJsonTreeItem::appendChild(QJsonTreeItem* item) {
0026     mChildren.append(item);
0027 }
0028 
0029 void QJsonTreeItem::reserve(int size) {
0030     mChildren.reserve(size);
0031 }
0032 
0033 QJsonTreeItem* QJsonTreeItem::child(int row) {
0034     return mChildren.value(row);
0035 }
0036 
0037 QJsonTreeItem* QJsonTreeItem::parent() {
0038     return mParent;
0039 }
0040 
0041 int QJsonTreeItem::childCount() const {
0042     return mChildren.count();
0043 }
0044 
0045 int QJsonTreeItem::row() const {
0046     if (mParent)
0047         return mParent->mChildren.indexOf(const_cast<QJsonTreeItem*>(this));
0048 
0049     return 0;
0050 }
0051 
0052 void QJsonTreeItem::setKey(const QString& key) {
0053     mKey = key;
0054 }
0055 
0056 void QJsonTreeItem::setValue(const QString& value) {
0057     mValue = value;
0058 }
0059 
0060 void QJsonTreeItem::setType(const QJsonValue::Type type) {
0061     mType = type;
0062 }
0063 
0064 void QJsonTreeItem::setSize(int size) {
0065     mSize = size;
0066 }
0067 
0068 const QString& QJsonTreeItem::key() const {
0069     return mKey;
0070 }
0071 
0072 const QString& QJsonTreeItem::value() const {
0073     return mValue;
0074 }
0075 
0076 QJsonValue::Type QJsonTreeItem::type() const {
0077     return mType;
0078 }
0079 
0080 int QJsonTreeItem::size() const {
0081     return mSize;
0082 }
0083 
0084 QJsonTreeItem* QJsonTreeItem::load(const QJsonValue& value, QJsonTreeItem* parent) {
0085     auto* rootItem = new QJsonTreeItem(parent);
0086     //  rootItem->setKey("root");
0087 
0088     if (value.isObject()) {
0089         const auto& object = value.toObject();
0090 
0091         // determine the size
0092         rootItem->setSize(QJsonDocument(object).toJson(QJsonDocument::Compact).size());
0093 
0094         // read all children
0095         for (const QString& key : object.keys()) {
0096             const QJsonValue& v = object.value(key);
0097             QJsonTreeItem* child = load(v, rootItem);
0098             child->setKey(key);
0099             child->setType(v.type());
0100             rootItem->appendChild(child);
0101         }
0102     } else if (value.isArray()) {
0103         const auto& array = value.toArray();
0104         rootItem->setSize(QJsonDocument(array).toJson(QJsonDocument::Compact).size());
0105 
0106         int index = 0;
0107         rootItem->reserve(array.count());
0108         for (const QJsonValue& v : array) {
0109             QJsonTreeItem* child = load(v, rootItem);
0110             child->setKey(QString::number(index));
0111             child->setType(v.type());
0112             rootItem->appendChild(child);
0113             ++index;
0114         }
0115     } else {
0116         const QString& str = value.toVariant().toString();
0117         rootItem->setValue(str);
0118         rootItem->setType(value.type());
0119         rootItem->setSize(str.length());
0120     }
0121 
0122     return rootItem;
0123 }
0124 
0125 //=========================================================================
0126 
0127 QJsonModel::QJsonModel(QObject* parent)
0128     : QAbstractItemModel(parent)
0129     , mHeadItem(new QJsonTreeItem)
0130     , mRootItem(new QJsonTreeItem(mHeadItem)) {
0131     mHeadItem->appendChild(mRootItem);
0132     mHeaders.append(i18n("Key"));
0133     mHeaders.append(i18n("Value"));
0134     mHeaders.append(i18n("Size in Bytes"));
0135 
0136     // icons
0137     QPainter painter;
0138     QPixmap pix(64, 64);
0139 
0140     QFont font;
0141     font.setPixelSize(60);
0142 
0143     const QColor& color = qApp->palette().color(QPalette::Text);
0144     painter.setPen(QPen(color));
0145 
0146     // draw the icon for JSON array
0147     pix.fill(QColor(Qt::transparent));
0148     painter.begin(&pix);
0149     painter.setFont(font);
0150     painter.drawText(pix.rect(), Qt::AlignCenter, QLatin1String("[ ]"));
0151     painter.end();
0152     mArrayIcon = QIcon(pix);
0153 
0154     // draw the icon for JSON object
0155     pix.fill(QColor(Qt::transparent));
0156     painter.begin(&pix);
0157     painter.setFont(font);
0158     painter.drawText(pix.rect(), Qt::AlignCenter, QLatin1String("{ }"));
0159     painter.end();
0160     mObjectIcon = QIcon(pix);
0161 }
0162 
0163 QJsonModel::~QJsonModel() {
0164     // delete mRootItem;
0165     delete mHeadItem;
0166 }
0167 
0168 void QJsonModel::clear() {
0169     beginResetModel();
0170     delete mHeadItem;
0171     mHeadItem = new QJsonTreeItem;
0172     mRootItem = new QJsonTreeItem(mHeadItem);
0173     mHeadItem->appendChild(mRootItem);
0174     endResetModel();
0175 }
0176 
0177 bool QJsonModel::load(const QString& fileName) {
0178     QFile file(fileName);
0179     bool success = false;
0180     if (file.open(QIODevice::ReadOnly)) {
0181         success = load(&file);
0182         file.close();
0183     } else
0184         success = false;
0185 
0186     return success;
0187 }
0188 
0189 bool QJsonModel::load(QIODevice* device) {
0190     return loadJson(device->readAll());
0191 }
0192 
0193 bool QJsonModel::loadJson(const QByteArray& json) {
0194     QJsonParseError jsonError;
0195 
0196     const QJsonDocument& doc = QJsonDocument::fromJson(json, &jsonError);
0197     if (jsonError.error == QJsonParseError::NoError) {
0198         const bool rc = loadJson(doc);
0199         if (!rc)
0200             Q_EMIT error(i18n("Failed to load the JSON file. Empty JSON document."));
0201         else
0202             Q_EMIT error(QString());
0203 
0204         return rc;
0205     } else {
0206         Q_EMIT error(i18n("Failed to load JSON document. Error: %1.", jsonError.errorString()));
0207         return false;
0208     }
0209 }
0210 
0211 bool QJsonModel::loadJson(const QJsonDocument& jdoc) {
0212     PERFTRACE(QStringLiteral("load json document into the model"));
0213     if (!jdoc.isNull()) {
0214         beginResetModel();
0215         delete mHeadItem;
0216 
0217         mHeadItem = new QJsonTreeItem;
0218 
0219         if (jdoc.isArray()) {
0220             {
0221                 PERFTRACE(QStringLiteral("load json tree items"));
0222                 mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array()), mHeadItem);
0223             }
0224             mRootItem->setType(QJsonValue::Array);
0225 
0226         } else {
0227             mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object()), mHeadItem);
0228             mRootItem->setType(QJsonValue::Object);
0229         }
0230 
0231         mHeadItem->appendChild(mRootItem);
0232 
0233         endResetModel();
0234         return true;
0235     }
0236 
0237     return false;
0238 }
0239 
0240 QVariant QJsonModel::data(const QModelIndex& index, int role) const {
0241     if (!index.isValid())
0242         return {};
0243 
0244     auto* item = static_cast<QJsonTreeItem*>(index.internalPointer());
0245 
0246     if (role == Qt::DisplayRole) {
0247         if (index.column() == 0)
0248             return item->key();
0249         else if (index.column() == 1) {
0250             // in case the value is very long, cut it so the preview tree tree view doesn't explode
0251             if (item->value().length() > 200)
0252                 return QString(item->value().left(200) + QStringLiteral(" ..."));
0253             else
0254                 return item->value();
0255         } else {
0256             if (item->size() != 0)
0257                 return QString::number(item->size());
0258             else
0259                 return {};
0260         }
0261     } else if (Qt::EditRole == role) {
0262         if (index.column() == 1)
0263             return item->value();
0264     } else if (role == Qt::DecorationRole) {
0265         if (index.column() == 0) {
0266             if (item->type() == QJsonValue::Array)
0267                 return mArrayIcon;
0268             else if (item->type() == QJsonValue::Object)
0269                 return mObjectIcon;
0270         }
0271     }
0272 
0273     return {};
0274 }
0275 
0276 bool QJsonModel::setData(const QModelIndex& index, const QVariant& value, int role) {
0277     if (Qt::EditRole == role) {
0278         if (index.column() == 1) {
0279             auto* item = static_cast<QJsonTreeItem*>(index.internalPointer());
0280             item->setValue(value.toString());
0281             Q_EMIT dataChanged(index, index, {Qt::EditRole});
0282             return true;
0283         }
0284     }
0285 
0286     return false;
0287 }
0288 
0289 QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const {
0290     if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
0291         return mHeaders.value(section);
0292 
0293     return {};
0294 }
0295 
0296 QModelIndex QJsonModel::index(int row, int column, const QModelIndex& parent) const {
0297     if (!hasIndex(row, column, parent))
0298         return QModelIndex{};
0299 
0300     QJsonTreeItem* parentItem;
0301 
0302     if (!parent.isValid())
0303         parentItem = mHeadItem;
0304     else
0305         parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());
0306 
0307     QJsonTreeItem* childItem = parentItem->child(row);
0308     if (childItem)
0309         return createIndex(row, column, childItem);
0310 
0311     return {};
0312 }
0313 
0314 QModelIndex QJsonModel::parent(const QModelIndex& index) const {
0315     if (!index.isValid())
0316         return QModelIndex{};
0317 
0318     auto* childItem = static_cast<QJsonTreeItem*>(index.internalPointer());
0319     QJsonTreeItem* parentItem = childItem->parent();
0320 
0321     if (parentItem == mHeadItem)
0322         return QModelIndex{};
0323 
0324     return createIndex(parentItem->row(), 0, parentItem);
0325 }
0326 
0327 int QJsonModel::rowCount(const QModelIndex& parent) const {
0328     QJsonTreeItem* parentItem;
0329     if (parent.column() > 0)
0330         return 0;
0331 
0332     if (!parent.isValid())
0333         parentItem = mHeadItem;
0334     else
0335         parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());
0336 
0337     return parentItem->childCount();
0338 }
0339 
0340 int QJsonModel::columnCount(const QModelIndex& /*parent*/) const {
0341     return 3;
0342 }
0343 
0344 Qt::ItemFlags QJsonModel::flags(const QModelIndex& index) const {
0345     if (index.column() == 1)
0346         return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
0347     else
0348         return QAbstractItemModel::flags(index);
0349 }
0350 /*
0351 QJsonDocument QJsonModel::json() const {
0352     auto v = genJson(mRootItem);
0353     QJsonDocument doc;
0354 
0355     if (v.isObject())
0356         doc = QJsonDocument(v.toObject());
0357     else
0358         doc = QJsonDocument(v.toArray());
0359 
0360     return doc;
0361 }
0362 */
0363 QJsonValue QJsonModel::genJson(QJsonTreeItem* item) const {
0364     auto type = item->type();
0365     const int nchild = item->childCount();
0366 
0367     if (QJsonValue::Object == type) {
0368         QJsonObject jo;
0369         for (int i = 0; i < nchild; ++i) {
0370             auto ch = item->child(i);
0371             auto key = ch->key();
0372             jo.insert(key, genJson(ch));
0373         }
0374         return jo;
0375     } else if (QJsonValue::Array == type) {
0376         QJsonArray arr;
0377         for (int i = 0; i < nchild; ++i) {
0378             auto ch = item->child(i);
0379             arr.append(genJson(ch));
0380         }
0381         return arr;
0382     } else {
0383         QJsonValue va(item->value());
0384         return va;
0385     }
0386 }
0387 
0388 QJsonDocument QJsonModel::genJsonByIndex(const QModelIndex& index) const {
0389     if (!index.isValid())
0390         return {};
0391 
0392     auto* item = static_cast<QJsonTreeItem*>(index.internalPointer());
0393     return QJsonDocument::fromVariant(genJson(item).toVariant());
0394 }