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 }