Add clang-format, clang-tidy; reindent files, increas readability
This commit is contained in:
parent
fbff9261ef
commit
eb8a3b5ea7
41
.clang-format
Normal file
41
.clang-format
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
BasedOnStyle: Mozilla
|
||||
Language: Cpp
|
||||
|
||||
IndentWidth: 8
|
||||
UseTab: AlignWithSpaces
|
||||
ColumnLimit: 81
|
||||
AccessModifierOffset: -4
|
||||
BinPackParameters: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
PenaltyReturnTypeOnItsOwnLine: 50
|
||||
ContinuationIndentWidth: 4
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
|
||||
BreakBeforeBraces: WebKit
|
||||
#BraceWrapping:
|
||||
# AfterCaseLabel: false
|
||||
# AfterClass: true
|
||||
# AfterControlStatement: MultiLine
|
||||
# AfterEnum: false
|
||||
# AfterFunction: true
|
||||
# AfterNamespace: false
|
||||
# AfterObjCDeclaration: false
|
||||
# AfterStruct: true
|
||||
# AfterUnion: true
|
||||
# AfterExternBlock: false
|
||||
#
|
||||
# BeforeCatch: true
|
||||
# BeforeElse: false
|
||||
# BeforeLambdaBody: false
|
||||
# BeforeWhile: false
|
||||
# IndentBraces: false
|
||||
# SplitEmptyFunction: true
|
||||
# SplitEmptyRecord: false
|
||||
# SplitEmptyNamespace: true
|
||||
#
|
||||
---
|
79
.clang-tidy
Normal file
79
.clang-tidy
Normal file
@ -0,0 +1,79 @@
|
||||
HeaderFilterRegex: '.*hpp'
|
||||
Checks: '-*,readability-identifier-naming,readability-identifier-length,readability-function-cognitive-complexity,google-readability-casting,-modernize-use-trailing-return-type,modernize-use-default-member-init,modernize-use-uncaught-exceptions,modernize-type-traits,modernize-use-override,misc-non-copyable-objects'
|
||||
|
||||
CheckOptions:
|
||||
# Modernize constructors
|
||||
- { key: modernize-use-default-member-init.UseAssignment, value : false}
|
||||
- { key: modernize-use-default-member-init.IgnoreMacros, value : false}
|
||||
|
||||
# Warn about complex functions
|
||||
- { key: readability-function-cognitive-complexity.Threshold, value: 10 }
|
||||
- { key: readability-function-cognitive-complexity.DescribeBasicIncrements, value: true }
|
||||
- { key: readability-function-cognitive-complexity.IgnoreMacros, value: true }
|
||||
|
||||
# Minimum Variable Length
|
||||
- { key: readability-identifier-length.MinimumVariableNameLength, value: 3 }
|
||||
- { key: readability-identifier-length.IgnoredVariableNames, value: "^(i|j|n|it)$" }
|
||||
|
||||
# Minimum Parameter Length
|
||||
- { key: readability-identifier-length.MinimumParameterNameLength, value: 3 }
|
||||
- { key: readability-identifier-length.IgnoredParameterNames, value: "^(i|j|n|it)$" }
|
||||
|
||||
# Minimum Loop Counter Length
|
||||
- { key: readability-identifier-length.MinimumLoopCounterNameLength, value: 3 }
|
||||
- { key: readability-identifier-length.IgnoredLoopCounterNames, value: "^(i|j|n|it)$" }
|
||||
|
||||
# Minimum ExceptionName Length:
|
||||
- { key: readability-identifier-length.MinimumExceptionNameLength, value: 3 }
|
||||
- { key: readability-identifier-length.IgnoredExceptionVariableNames, value: "^[e]$" }
|
||||
|
||||
# Class Names
|
||||
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
|
||||
|
||||
- { key: readability-identifier-naming.EnumIgnoredRegexp, value: '^.*_t$' }
|
||||
|
||||
# Abstract Class Name
|
||||
# - { key: readability-identifier-naming.AbstractClassPrefix,value: 'I' }
|
||||
# - { key: readability-identifier-naming.AbstractClassIgnoredRegexp,
|
||||
# value: '^.*able$|^.*Base$|^Abstract.*|^Component$' }
|
||||
|
||||
# Template Parameters
|
||||
- { key: readability-identifier-naming.TypeTemplateParameterPrefix,
|
||||
value: '' }
|
||||
- { key: readability-identifier-naming.TypeTemplateParameterSuffix,
|
||||
value: '' }
|
||||
- { key: readability-identifier-naming.TypeTemplateParameterCase,
|
||||
value: CamelCase }
|
||||
- { key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp, value: "^T$" }
|
||||
|
||||
# TypeAlias Rules
|
||||
- { key: readability-identifier-naming.TypeAliasCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.TypeAliasIgnoredRegexp,
|
||||
value: '.*_t|string|.*_string' }
|
||||
|
||||
# Function Names
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PublicMethodCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PrivateMethodCase, value: camelBack }
|
||||
|
||||
# Variable Names
|
||||
- { key: readability-identifier-naming.VariableCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PrivateMemberCase, value: camelBack }
|
||||
|
||||
# Constants and Enum Values
|
||||
#- { key: readability-identifier-naming.ConstantPrefix, value: 'k' }
|
||||
- { key: readability-identifier-naming.ConstantCase, value: camelBack }
|
||||
|
||||
- { key: readability-identifier-naming.EnumConstantPrefix, value: '' }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: CamelCase }
|
||||
|
||||
# Constant Expression
|
||||
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: 'k' }
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: Camel_Snake_Case }
|
||||
|
||||
- { key: readability-identifier-naming.ConstexprFunctionCase, value: Camel_Snake_Case }
|
||||
- { key: readability-identifier-naming.ConstexprMethodCase, value: Camel_Snake_Case }
|
||||
|
||||
# vim: set ts=4 noet sw=4 sts=0 colorcolumn=100 :
|
916
QJsonModel.cpp
916
QJsonModel.cpp
@ -23,483 +23,595 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// NOLINTBEGIN
|
||||
|
||||
#include "QJsonModel.hpp"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFont>
|
||||
|
||||
inline bool contains(const QStringList &list, const QString &value) {
|
||||
for (auto val : list)
|
||||
if (value.contains(val, Qt::CaseInsensitive))
|
||||
return true;
|
||||
inline bool contains(const QStringList& list, const QString& value)
|
||||
{
|
||||
for (auto val : list)
|
||||
if (value.contains(val, Qt::CaseInsensitive))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) { mParent = parent; }
|
||||
|
||||
QJsonTreeItem::~QJsonTreeItem() { qDeleteAll(mChilds); }
|
||||
|
||||
void QJsonTreeItem::appendChild(QJsonTreeItem *item) { mChilds.append(item); }
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::child(int row) { return mChilds.value(row); }
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::parent() { return mParent; }
|
||||
|
||||
int QJsonTreeItem::childCount() const { return mChilds.count(); }
|
||||
|
||||
int QJsonTreeItem::row() const {
|
||||
if (mParent)
|
||||
return mParent->mChilds.indexOf(const_cast<QJsonTreeItem *>(this));
|
||||
|
||||
return 0;
|
||||
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem* parent)
|
||||
{
|
||||
mParent = parent;
|
||||
}
|
||||
|
||||
void QJsonTreeItem::setKey(const QString &key) { mKey = key; }
|
||||
QJsonTreeItem::~QJsonTreeItem()
|
||||
{
|
||||
qDeleteAll(mChilds);
|
||||
}
|
||||
|
||||
void QJsonTreeItem::setValue(const QVariant &value) { mValue = value; }
|
||||
void QJsonTreeItem::appendChild(QJsonTreeItem* item)
|
||||
{
|
||||
mChilds.append(item);
|
||||
}
|
||||
|
||||
void QJsonTreeItem::setType(const QJsonValue::Type &type) { mType = type; }
|
||||
QJsonTreeItem* QJsonTreeItem::child(int row)
|
||||
{
|
||||
return mChilds.value(row);
|
||||
}
|
||||
|
||||
QString QJsonTreeItem::key() const { return mKey; }
|
||||
QJsonTreeItem* QJsonTreeItem::parent()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
QVariant QJsonTreeItem::value() const { return mValue; }
|
||||
int QJsonTreeItem::childCount() const
|
||||
{
|
||||
return mChilds.count();
|
||||
}
|
||||
|
||||
QJsonValue::Type QJsonTreeItem::type() const { return mType; }
|
||||
int QJsonTreeItem::row() const
|
||||
{
|
||||
if (mParent)
|
||||
return mParent->mChilds.indexOf(const_cast<QJsonTreeItem*>(this)
|
||||
);
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value,
|
||||
const QStringList &exceptions,
|
||||
QJsonTreeItem *parent) {
|
||||
QJsonTreeItem *rootItem = new QJsonTreeItem(parent);
|
||||
rootItem->setKey("root");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (value.isObject()) {
|
||||
// Get all QJsonValue childs
|
||||
const QStringList keys =
|
||||
value.toObject().keys(); // To prevent clazy-range warning
|
||||
for (const QString &key : keys) {
|
||||
if (contains(exceptions, key)) {
|
||||
continue;
|
||||
}
|
||||
QJsonValue v = value.toObject().value(key);
|
||||
QJsonTreeItem *child = load(v, exceptions, rootItem);
|
||||
child->setKey(key);
|
||||
child->setType(v.type());
|
||||
rootItem->appendChild(child);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
// Get all QJsonValue childs
|
||||
int index = 0;
|
||||
const QJsonArray array = value.toArray(); // To prevent clazy-range warning
|
||||
for (const QJsonValue &v : array) {
|
||||
QJsonTreeItem *child = load(v, exceptions, rootItem);
|
||||
child->setKey(QString::number(index));
|
||||
child->setType(v.type());
|
||||
rootItem->appendChild(child);
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
rootItem->setValue(value.toVariant());
|
||||
rootItem->setType(value.type());
|
||||
}
|
||||
void QJsonTreeItem::setKey(const QString& key)
|
||||
{
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
return rootItem;
|
||||
void QJsonTreeItem::setValue(const QVariant& value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
void QJsonTreeItem::setType(const QJsonValue::Type& type)
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
|
||||
QString QJsonTreeItem::key() const
|
||||
{
|
||||
return mKey;
|
||||
}
|
||||
|
||||
QVariant QJsonTreeItem::value() const
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
QJsonValue::Type QJsonTreeItem::type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
QJsonTreeItem* QJsonTreeItem::load(
|
||||
const QJsonValue& value, const QStringList& exceptions, QJsonTreeItem* parent
|
||||
)
|
||||
{
|
||||
QJsonTreeItem* rootItem = new QJsonTreeItem(parent);
|
||||
rootItem->setKey("root");
|
||||
|
||||
if (value.isObject()) {
|
||||
// Get all QJsonValue childs
|
||||
const QStringList keys =
|
||||
value.toObject().keys(); // To prevent clazy-range warning
|
||||
for (const QString& key : keys) {
|
||||
if (contains(exceptions, key)) {
|
||||
continue;
|
||||
}
|
||||
QJsonValue jsonValue = value.toObject().value(key);
|
||||
QJsonTreeItem* child = load(jsonValue, exceptions, rootItem);
|
||||
child->setKey(key);
|
||||
child->setType(jsonValue.type());
|
||||
rootItem->appendChild(child);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
// Get all QJsonValue childs
|
||||
int index = 0;
|
||||
const QJsonArray array =
|
||||
value.toArray(); // To prevent clazy-range warning
|
||||
for (const QJsonValue& jsonValue : array) {
|
||||
QJsonTreeItem* child =
|
||||
load(jsonValue, exceptions, rootItem);
|
||||
child->setKey(QString::number(index));
|
||||
child->setType(jsonValue.type());
|
||||
rootItem->appendChild(child);
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
rootItem->setValue(value.toVariant());
|
||||
rootItem->setType(value.type());
|
||||
}
|
||||
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
inline uchar hexdig(uint u) { return (u < 0xa ? '0' + u : 'a' + u - 0xa); }
|
||||
|
||||
QByteArray escapedString(const QString &s) {
|
||||
QByteArray ba(s.length(), Qt::Uninitialized);
|
||||
uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
|
||||
const uchar *ba_end = cursor + ba.length();
|
||||
const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
|
||||
const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
|
||||
while (src != end) {
|
||||
if (cursor >= ba_end - 6) {
|
||||
// ensure we have enough space
|
||||
int pos = cursor - reinterpret_cast<const uchar *>(ba.constData());
|
||||
ba.resize(ba.size() * 2);
|
||||
cursor = reinterpret_cast<uchar *>(ba.data()) + pos;
|
||||
ba_end = reinterpret_cast<const uchar *>(ba.constData()) + ba.length();
|
||||
}
|
||||
uint u = *src++;
|
||||
if (u < 0x80) {
|
||||
if (u < 0x20 || u == 0x22 || u == 0x5c) {
|
||||
*cursor++ = '\\';
|
||||
switch (u) {
|
||||
case 0x22:
|
||||
*cursor++ = '"';
|
||||
break;
|
||||
case 0x5c:
|
||||
*cursor++ = '\\';
|
||||
break;
|
||||
case 0x8:
|
||||
*cursor++ = 'b';
|
||||
break;
|
||||
case 0xc:
|
||||
*cursor++ = 'f';
|
||||
break;
|
||||
case 0xa:
|
||||
*cursor++ = 'n';
|
||||
break;
|
||||
case 0xd:
|
||||
*cursor++ = 'r';
|
||||
break;
|
||||
case 0x9:
|
||||
*cursor++ = 't';
|
||||
break;
|
||||
default:
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = hexdig(u >> 4);
|
||||
*cursor++ = hexdig(u & 0xf);
|
||||
}
|
||||
} else {
|
||||
*cursor++ = (uchar)u;
|
||||
}
|
||||
} else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) <
|
||||
0) {
|
||||
// failed to get valid utf8 use JSON escape sequence
|
||||
*cursor++ = '\\';
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = hexdig(u >> 12 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 8 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 4 & 0x0f);
|
||||
*cursor++ = hexdig(u & 0x0f);
|
||||
}
|
||||
}
|
||||
ba.resize(cursor - reinterpret_cast<const uchar *>(ba.constData()));
|
||||
return ba;
|
||||
inline uchar hexdig(uint positiveValue)
|
||||
{
|
||||
return (
|
||||
positiveValue < 0xa ? '0' + positiveValue : 'a' + positiveValue - 0xa
|
||||
);
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
QByteArray escapedString(const QString& text)
|
||||
{
|
||||
QByteArray byteArray(text.length(), Qt::Uninitialized);
|
||||
uchar* cursor =
|
||||
reinterpret_cast<uchar*>(const_cast<char*>(byteArray.constData()));
|
||||
const uchar* byteArrayEnd = cursor + byteArray.length();
|
||||
const ushort* src = reinterpret_cast<const ushort*>(text.constBegin());
|
||||
const ushort* const end =
|
||||
reinterpret_cast<const ushort*>(text.constEnd());
|
||||
while (src != end) {
|
||||
if (cursor >= byteArrayEnd - 6) {
|
||||
// ensure we have enough space
|
||||
int pos = cursor - reinterpret_cast<const uchar*>(
|
||||
byteArray.constData()
|
||||
);
|
||||
byteArray.resize(byteArray.size() * 2);
|
||||
cursor =
|
||||
reinterpret_cast<uchar*>(byteArray.data()) + pos;
|
||||
byteArrayEnd =
|
||||
reinterpret_cast<const uchar*>(byteArray.constData()
|
||||
) +
|
||||
byteArray.length();
|
||||
}
|
||||
uint uValue = *src++; // uValue = unsigned value
|
||||
if (uValue < 0x80) {
|
||||
if (uValue < 0x20 || uValue == 0x22 || uValue == 0x5c) {
|
||||
*cursor++ = '\\';
|
||||
switch (uValue) {
|
||||
case 0x22:
|
||||
*cursor++ = '"';
|
||||
break;
|
||||
case 0x5c:
|
||||
*cursor++ = '\\';
|
||||
break;
|
||||
case 0x8:
|
||||
*cursor++ = 'b';
|
||||
break;
|
||||
case 0xc:
|
||||
*cursor++ = 'f';
|
||||
break;
|
||||
case 0xa:
|
||||
*cursor++ = 'n';
|
||||
break;
|
||||
case 0xd:
|
||||
*cursor++ = 'r';
|
||||
break;
|
||||
case 0x9:
|
||||
*cursor++ = 't';
|
||||
break;
|
||||
default:
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = hexdig(uValue >> 4);
|
||||
*cursor++ = hexdig(uValue & 0xf);
|
||||
}
|
||||
} else {
|
||||
*cursor++ = static_cast<uchar>(uValue);
|
||||
}
|
||||
} else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(uValue, cursor, src, end) < 0) {
|
||||
// failed to get valid utf8 use JSON escape sequence
|
||||
*cursor++ = '\\';
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = hexdig(uValue >> 12 & 0x0f);
|
||||
*cursor++ = hexdig(uValue >> 8 & 0x0f);
|
||||
*cursor++ = hexdig(uValue >> 4 & 0x0f);
|
||||
*cursor++ = hexdig(uValue & 0x0f);
|
||||
}
|
||||
}
|
||||
byteArray.resize(
|
||||
cursor - reinterpret_cast<const uchar*>(byteArray.constData())
|
||||
);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(const QString &fileName, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
load(fileName);
|
||||
QJsonModel::QJsonModel(QObject* parent)
|
||||
: QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
|
||||
{
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(QIODevice *device, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
load(device);
|
||||
QJsonModel::QJsonModel(const QString& fileName, QObject* parent)
|
||||
: QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
|
||||
{
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
load(fileName);
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(const QByteArray &json, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
loadJson(json);
|
||||
QJsonModel::QJsonModel(QIODevice* device, QObject* parent)
|
||||
: QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
|
||||
{
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
load(device);
|
||||
}
|
||||
|
||||
QJsonModel::~QJsonModel() { delete mRootItem; }
|
||||
|
||||
bool QJsonModel::load(const QString &fileName) {
|
||||
QFile file(fileName);
|
||||
bool success = false;
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
success = load(&file);
|
||||
file.close();
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
QJsonModel::QJsonModel(const QByteArray& json, QObject* parent)
|
||||
: QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem }
|
||||
{
|
||||
mHeaders.append("key");
|
||||
mHeaders.append("value");
|
||||
loadJson(json);
|
||||
}
|
||||
|
||||
bool QJsonModel::load(QIODevice *device) { return loadJson(device->readAll()); }
|
||||
|
||||
bool QJsonModel::loadJson(const QByteArray &json) {
|
||||
auto const &jdoc = QJsonDocument::fromJson(json);
|
||||
|
||||
if (!jdoc.isNull()) {
|
||||
beginResetModel();
|
||||
delete mRootItem;
|
||||
if (jdoc.isArray()) {
|
||||
mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array()), mExceptions);
|
||||
mRootItem->setType(QJsonValue::Array);
|
||||
|
||||
} else {
|
||||
mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object()), mExceptions);
|
||||
mRootItem->setType(QJsonValue::Object);
|
||||
}
|
||||
endResetModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "cannot load json";
|
||||
return false;
|
||||
QJsonModel::~QJsonModel()
|
||||
{
|
||||
delete mRootItem;
|
||||
}
|
||||
|
||||
QVariant QJsonModel::data(const QModelIndex &index, int role) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
bool QJsonModel::load(const QString& fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
bool success = false;
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
success = load(&file);
|
||||
file.close();
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (index.column() == 0)
|
||||
return QString("%1").arg(item->key());
|
||||
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
} else if (Qt::EditRole == role) {
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
}
|
||||
|
||||
return {};
|
||||
return success;
|
||||
}
|
||||
|
||||
bool QJsonModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
int role) {
|
||||
int col = index.column();
|
||||
if (Qt::EditRole == role) {
|
||||
if (col == 1) {
|
||||
QJsonTreeItem *item =
|
||||
static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
item->setValue(value);
|
||||
emit dataChanged(index, index, {Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
bool QJsonModel::load(QIODevice* device)
|
||||
{
|
||||
return loadJson(device->readAll());
|
||||
}
|
||||
|
||||
QVariant QJsonModel::headerData(int section, Qt::Orientation orientation,
|
||||
int role) const {
|
||||
if (role != Qt::DisplayRole)
|
||||
return {};
|
||||
bool QJsonModel::loadJson(const QByteArray& json)
|
||||
{
|
||||
auto const& jdoc = QJsonDocument::fromJson(json);
|
||||
|
||||
if (orientation == Qt::Horizontal)
|
||||
return mHeaders.value(section);
|
||||
else
|
||||
return {};
|
||||
if (!jdoc.isNull()) {
|
||||
beginResetModel();
|
||||
delete mRootItem;
|
||||
if (jdoc.isArray()) {
|
||||
mRootItem = QJsonTreeItem::load(
|
||||
QJsonValue(jdoc.array()), mExceptions
|
||||
);
|
||||
mRootItem->setType(QJsonValue::Array);
|
||||
|
||||
} else {
|
||||
mRootItem = QJsonTreeItem::load(
|
||||
QJsonValue(jdoc.object()), mExceptions
|
||||
);
|
||||
mRootItem->setType(QJsonValue::Object);
|
||||
}
|
||||
endResetModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "cannot load json";
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex QJsonModel::index(int row, int column,
|
||||
const QModelIndex &parent) const {
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
QVariant QJsonModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
QJsonTreeItem *parentItem;
|
||||
QJsonTreeItem* item =
|
||||
static_cast<QJsonTreeItem*>(index.internalPointer());
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (index.column() == 0)
|
||||
return QString("%1").arg(item->key());
|
||||
|
||||
QJsonTreeItem *childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return {};
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
} else if (Qt::EditRole == role) {
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex QJsonModel::parent(const QModelIndex &index) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
bool QJsonModel::setData(
|
||||
const QModelIndex& index, const QVariant& value, int role
|
||||
)
|
||||
{
|
||||
int col = index.column();
|
||||
if (Qt::EditRole == role) {
|
||||
if (col == 1) {
|
||||
QJsonTreeItem* item =
|
||||
static_cast<QJsonTreeItem*>(index.internalPointer());
|
||||
item->setValue(value);
|
||||
emit dataChanged(index, index, { Qt::EditRole });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonTreeItem *childItem =
|
||||
static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
QJsonTreeItem *parentItem = childItem->parent();
|
||||
|
||||
if (parentItem == mRootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
int QJsonModel::rowCount(const QModelIndex &parent) const {
|
||||
QJsonTreeItem *parentItem;
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
QVariant
|
||||
QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return {};
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
|
||||
|
||||
return parentItem->childCount();
|
||||
if (orientation == Qt::Horizontal)
|
||||
return mHeaders.value(section);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
int QJsonModel::columnCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent)
|
||||
return 2;
|
||||
QModelIndex
|
||||
QJsonModel::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
|
||||
QJsonTreeItem* parentItem;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem =
|
||||
static_cast<QJsonTreeItem*>(parent.internalPointer());
|
||||
|
||||
QJsonTreeItem* childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const {
|
||||
int col = index.column();
|
||||
auto item = static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
QModelIndex QJsonModel::parent(const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
auto isArray = QJsonValue::Array == item->type();
|
||||
auto isObject = QJsonValue::Object == item->type();
|
||||
QJsonTreeItem* childItem =
|
||||
static_cast<QJsonTreeItem*>(index.internalPointer());
|
||||
QJsonTreeItem* parentItem = childItem->parent();
|
||||
|
||||
if ((col == 1) && !(isArray || isObject))
|
||||
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
||||
else
|
||||
return QAbstractItemModel::flags(index);
|
||||
if (parentItem == mRootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
QByteArray QJsonModel::json(bool compact) {
|
||||
auto jsonValue = genJson(mRootItem);
|
||||
QByteArray json;
|
||||
if (jsonValue.isNull())
|
||||
return json;
|
||||
int QJsonModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
QJsonTreeItem* parentItem;
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (jsonValue.isArray())
|
||||
arrayToJson(jsonValue.toArray(), json, 0, compact);
|
||||
else
|
||||
objectToJson(jsonValue.toObject(), json, 0, compact);
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem =
|
||||
static_cast<QJsonTreeItem*>(parent.internalPointer());
|
||||
|
||||
return json;
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "}" : "}\n";
|
||||
}
|
||||
void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact) {
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "]" : "]\n";
|
||||
int QJsonModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 2;
|
||||
}
|
||||
|
||||
void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
if (jsonArray.size() <= 0)
|
||||
return;
|
||||
Qt::ItemFlags QJsonModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
int col = index.column();
|
||||
auto item = static_cast<QJsonTreeItem*>(index.internalPointer());
|
||||
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
json += indentString;
|
||||
valueToJson(jsonArray.at(i), json, indent, compact);
|
||||
if (++i == jsonArray.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
if (jsonObject.size() <= 0)
|
||||
return;
|
||||
auto isArray = QJsonValue::Array == item->type();
|
||||
auto isObject = QJsonValue::Object == item->type();
|
||||
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
QString key = jsonObject.keys().at(i);
|
||||
json += indentString;
|
||||
json += '"';
|
||||
json += escapedString(key);
|
||||
json += compact ? "\":" : "\": ";
|
||||
valueToJson(jsonObject.value(key), json, indent, compact);
|
||||
if (++i == jsonObject.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
if ((col == 1) && !(isArray || isObject))
|
||||
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
||||
else
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
|
||||
bool compact) {
|
||||
QJsonValue::Type type = jsonValue.type();
|
||||
switch (type) {
|
||||
case QJsonValue::Bool:
|
||||
json += jsonValue.toBool() ? "true" : "false";
|
||||
break;
|
||||
case QJsonValue::Double: {
|
||||
const double d = jsonValue.toDouble();
|
||||
if (qIsFinite(d)) {
|
||||
json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest);
|
||||
} else {
|
||||
json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QJsonValue::String:
|
||||
json += '"';
|
||||
json += escapedString(jsonValue.toString());
|
||||
json += '"';
|
||||
break;
|
||||
case QJsonValue::Array:
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonValue.toArray(), json, indent + (compact ? 0 : 1),
|
||||
compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += ']';
|
||||
break;
|
||||
case QJsonValue::Object:
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonValue.toObject(), json, indent + (compact ? 0 : 1),
|
||||
compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += '}';
|
||||
break;
|
||||
case QJsonValue::Null:
|
||||
default:
|
||||
json += "null";
|
||||
}
|
||||
QByteArray QJsonModel::json(bool compact)
|
||||
{
|
||||
auto jsonValue = genJson(mRootItem);
|
||||
QByteArray json;
|
||||
if (jsonValue.isNull())
|
||||
return json;
|
||||
|
||||
if (jsonValue.isArray())
|
||||
arrayToJson(jsonValue.toArray(), json, 0, compact);
|
||||
else
|
||||
objectToJson(jsonValue.toObject(), json, 0, compact);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void QJsonModel::addException(const QStringList &exceptions) {
|
||||
mExceptions = exceptions;
|
||||
void QJsonModel::objectToJson(
|
||||
QJsonObject jsonObject, QByteArray& json, int indent, bool compact
|
||||
)
|
||||
{
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(
|
||||
jsonObject, json, indent + (compact ? 0 : 1), compact
|
||||
);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "}" : "}\n";
|
||||
}
|
||||
void QJsonModel::arrayToJson(
|
||||
QJsonArray jsonArray, QByteArray& json, int indent, bool compact
|
||||
)
|
||||
{
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "]" : "]\n";
|
||||
}
|
||||
|
||||
QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const {
|
||||
auto type = item->type();
|
||||
int nchild = item->childCount();
|
||||
void QJsonModel::arrayContentToJson(
|
||||
QJsonArray jsonArray, QByteArray& json, int indent, bool compact
|
||||
)
|
||||
{
|
||||
if (jsonArray.size() <= 0)
|
||||
return;
|
||||
|
||||
if (QJsonValue::Object == type) {
|
||||
QJsonObject jo;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
auto key = ch->key();
|
||||
jo.insert(key, genJson(ch));
|
||||
}
|
||||
return jo;
|
||||
} else if (QJsonValue::Array == type) {
|
||||
QJsonArray arr;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
arr.append(genJson(ch));
|
||||
}
|
||||
return arr;
|
||||
} else {
|
||||
QJsonValue va;
|
||||
switch (item->value().typeId()) {
|
||||
case QMetaType::Bool: {
|
||||
va = item->value().toBool();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
va = item->value().toString();
|
||||
break;
|
||||
}
|
||||
(item->value());
|
||||
return va;
|
||||
}
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
json += indentString;
|
||||
valueToJson(jsonArray.at(i), json, indent, compact);
|
||||
if (++i == jsonArray.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
void QJsonModel::objectContentToJson(
|
||||
QJsonObject jsonObject, QByteArray& json, int indent, bool compact
|
||||
)
|
||||
{
|
||||
if (jsonObject.size() <= 0)
|
||||
return;
|
||||
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
QString key = jsonObject.keys().at(i);
|
||||
json += indentString;
|
||||
json += '"';
|
||||
json += escapedString(key);
|
||||
json += compact ? "\":" : "\": ";
|
||||
valueToJson(jsonObject.value(key), json, indent, compact);
|
||||
if (++i == jsonObject.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
|
||||
void QJsonModel::valueToJson(
|
||||
QJsonValue jsonValue, QByteArray& json, int indent, bool compact
|
||||
)
|
||||
{
|
||||
QJsonValue::Type type = jsonValue.type();
|
||||
switch (type) {
|
||||
case QJsonValue::Bool:
|
||||
json += jsonValue.toBool() ? "true" : "false";
|
||||
break;
|
||||
case QJsonValue::Double: {
|
||||
const double value = jsonValue.toDouble();
|
||||
if (qIsFinite(value)) {
|
||||
json += QByteArray::number(
|
||||
value, 'f', QLocale::FloatingPointShortest
|
||||
);
|
||||
} else {
|
||||
json += "null"; // +INF || -INF || NaN (see
|
||||
// RFC4627#section2.4)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QJsonValue::String:
|
||||
json += '"';
|
||||
json += escapedString(jsonValue.toString());
|
||||
json += '"';
|
||||
break;
|
||||
case QJsonValue::Array:
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(
|
||||
jsonValue.toArray(),
|
||||
json,
|
||||
indent + (compact ? 0 : 1),
|
||||
compact
|
||||
);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += ']';
|
||||
break;
|
||||
case QJsonValue::Object:
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(
|
||||
jsonValue.toObject(),
|
||||
json,
|
||||
indent + (compact ? 0 : 1),
|
||||
compact
|
||||
);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += '}';
|
||||
break;
|
||||
case QJsonValue::Null:
|
||||
default:
|
||||
json += "null";
|
||||
}
|
||||
}
|
||||
|
||||
void QJsonModel::addException(const QStringList& exceptions)
|
||||
{
|
||||
mExceptions = exceptions;
|
||||
}
|
||||
|
||||
QJsonValue QJsonModel::genJson(QJsonTreeItem* item) const
|
||||
{
|
||||
auto type = item->type();
|
||||
int nchild = item->childCount();
|
||||
|
||||
if (QJsonValue::Object == type) {
|
||||
QJsonObject jo;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
auto key = ch->key();
|
||||
jo.insert(key, genJson(ch));
|
||||
}
|
||||
return jo;
|
||||
} else if (QJsonValue::Array == type) {
|
||||
QJsonArray arr;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
arr.append(genJson(ch));
|
||||
}
|
||||
return arr;
|
||||
} else {
|
||||
QJsonValue va;
|
||||
switch (item->value().typeId()) {
|
||||
case QMetaType::Bool: {
|
||||
va = item->value().toBool();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
va = item->value().toString();
|
||||
break;
|
||||
}
|
||||
(item->value());
|
||||
return va;
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=4 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
||||
|
@ -38,78 +38,92 @@ class QJsonModel;
|
||||
class QJsonItem;
|
||||
|
||||
class QJsonTreeItem {
|
||||
public:
|
||||
QJsonTreeItem(QJsonTreeItem *parent = nullptr);
|
||||
~QJsonTreeItem();
|
||||
void appendChild(QJsonTreeItem *item);
|
||||
QJsonTreeItem *child(int row);
|
||||
QJsonTreeItem *parent();
|
||||
int childCount() const;
|
||||
int row() const;
|
||||
void setKey(const QString &key);
|
||||
void setValue(const QVariant &value);
|
||||
void setType(const QJsonValue::Type &type);
|
||||
QString key() const;
|
||||
QVariant value() const;
|
||||
QJsonValue::Type type() const;
|
||||
public:
|
||||
QJsonTreeItem(QJsonTreeItem* parent = nullptr);
|
||||
~QJsonTreeItem();
|
||||
void appendChild(QJsonTreeItem* item);
|
||||
QJsonTreeItem* child(int row);
|
||||
QJsonTreeItem* parent();
|
||||
int childCount() const;
|
||||
int row() const;
|
||||
void setKey(const QString& key);
|
||||
void setValue(const QVariant& value);
|
||||
void setType(const QJsonValue::Type& type);
|
||||
QString key() const;
|
||||
QVariant value() const;
|
||||
QJsonValue::Type type() const;
|
||||
|
||||
static QJsonTreeItem *load(const QJsonValue &value,
|
||||
const QStringList &exceptions = {},
|
||||
QJsonTreeItem *parent = nullptr);
|
||||
static QJsonTreeItem* load(
|
||||
const QJsonValue& value, const QStringList& exceptions = {},
|
||||
QJsonTreeItem* parent = nullptr
|
||||
);
|
||||
|
||||
protected:
|
||||
private:
|
||||
QString mKey;
|
||||
QVariant mValue;
|
||||
QJsonValue::Type mType;
|
||||
QList<QJsonTreeItem *> mChilds;
|
||||
QJsonTreeItem *mParent = nullptr;
|
||||
protected:
|
||||
private:
|
||||
QString mKey;
|
||||
QVariant mValue;
|
||||
QJsonValue::Type mType;
|
||||
QList<QJsonTreeItem*> mChilds;
|
||||
QJsonTreeItem* mParent = nullptr;
|
||||
};
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
class QJsonModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QJsonModel(QObject *parent = nullptr);
|
||||
QJsonModel(const QString &fileName, QObject *parent = nullptr);
|
||||
QJsonModel(QIODevice *device, QObject *parent = nullptr);
|
||||
QJsonModel(const QByteArray &json, QObject *parent = nullptr);
|
||||
~QJsonModel();
|
||||
bool load(const QString &fileName);
|
||||
bool load(QIODevice *device);
|
||||
bool loadJson(const QByteArray &json);
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value,
|
||||
int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role) const override;
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QByteArray json(bool compact = false);
|
||||
QByteArray jsonToByte(QJsonValue jsonValue);
|
||||
void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
//! List of tags to skip during JSON parsing
|
||||
void addException(const QStringList &exceptions);
|
||||
// NOLINTNEXTLINE
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QJsonModel(QObject* parent = nullptr);
|
||||
QJsonModel(const QString& fileName, QObject* parent = nullptr);
|
||||
QJsonModel(QIODevice* device, QObject* parent = nullptr);
|
||||
QJsonModel(const QByteArray& json, QObject* parent = nullptr);
|
||||
~QJsonModel() override;
|
||||
bool load(const QString& fileName);
|
||||
bool load(QIODevice* device);
|
||||
bool loadJson(const QByteArray& json);
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
bool setData(
|
||||
const QModelIndex& index, const QVariant& value,
|
||||
int role = Qt::EditRole
|
||||
) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role)
|
||||
const override;
|
||||
QModelIndex index(
|
||||
int row, int column, const QModelIndex& parent = QModelIndex()
|
||||
) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int
|
||||
columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
QByteArray json(bool compact = false);
|
||||
QByteArray jsonToByte(QJsonValue jsonValue);
|
||||
void objectToJson(
|
||||
QJsonObject jsonObject, QByteArray& json, int indent, bool compact
|
||||
);
|
||||
void arrayToJson(
|
||||
QJsonArray jsonArray, QByteArray& json, int indent, bool compact
|
||||
);
|
||||
void arrayContentToJson(
|
||||
QJsonArray jsonArray, QByteArray& json, int indent, bool compact
|
||||
);
|
||||
void objectContentToJson(
|
||||
QJsonObject jsonObject, QByteArray& json, int indent, bool compact
|
||||
);
|
||||
void valueToJson(
|
||||
QJsonValue jsonValue, QByteArray& json, int indent, bool compact
|
||||
);
|
||||
//! List of tags to skip during JSON parsing
|
||||
void addException(const QStringList& exceptions);
|
||||
|
||||
private:
|
||||
QJsonValue genJson(QJsonTreeItem *) const;
|
||||
QJsonTreeItem *mRootItem = nullptr;
|
||||
QStringList mHeaders;
|
||||
//! List of exceptions (e.g. comments). Case insensitive, compairs on
|
||||
//! "contains".
|
||||
QStringList mExceptions;
|
||||
private:
|
||||
QJsonValue genJson(QJsonTreeItem*) const;
|
||||
QJsonTreeItem* mRootItem = nullptr;
|
||||
QStringList mHeaders;
|
||||
//! List of exceptions (e.g. comments). Case insensitive, compairs on
|
||||
//! "contains".
|
||||
QStringList mExceptions;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
||||
|
@ -29,166 +29,198 @@ namespace QUtf8Functions {
|
||||
/// Error if \a u is a low surrogate;
|
||||
/// if \a u is a high surrogate, Error if the next isn't a low one,
|
||||
/// EndOfString if we run into the end of the string.
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end) {
|
||||
if (!Traits::skipAsciiHandling && u < 0x80) {
|
||||
// U+0000 to U+007F (US-ASCII) - one byte
|
||||
Traits::appendByte(dst, uchar(u));
|
||||
return 0;
|
||||
} else if (u < 0x0800) {
|
||||
// U+0080 to U+07FF - two bytes
|
||||
// first of two bytes
|
||||
Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
|
||||
} else {
|
||||
if (!QChar::isSurrogate(u)) {
|
||||
// U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(u))
|
||||
return Traits::Error;
|
||||
// first of three bytes
|
||||
Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
|
||||
} else {
|
||||
// U+10000 to U+10FFFF - four bytes
|
||||
// need to get one extra codepoint
|
||||
if (Traits::availableUtf16(src, end) == 0)
|
||||
return Traits::EndOfString;
|
||||
ushort low = Traits::peekUtf16(src);
|
||||
if (!QChar::isHighSurrogate(u))
|
||||
return Traits::Error;
|
||||
if (!QChar::isLowSurrogate(low))
|
||||
return Traits::Error;
|
||||
Traits::advanceUtf16(src);
|
||||
uint ucs4 = QChar::surrogateToUcs4(u, low);
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4))
|
||||
return Traits::Error;
|
||||
// first byte
|
||||
Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf));
|
||||
// second of four bytes
|
||||
Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f));
|
||||
// for the rest of the bytes
|
||||
u = ushort(ucs4);
|
||||
}
|
||||
// second to last byte
|
||||
Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
|
||||
}
|
||||
// last byte
|
||||
Traits::appendByte(dst, 0x80 | (u & 0x3f));
|
||||
return 0;
|
||||
template<typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int toUtf8(ushort u, OutputPtr& dst, InputPtr& src, InputPtr end)
|
||||
{
|
||||
if (!Traits::skipAsciiHandling && u < 0x80) {
|
||||
// U+0000 to U+007F (US-ASCII) - one byte
|
||||
Traits::appendByte(dst, uchar(u));
|
||||
return 0;
|
||||
} else if (u < 0x0800) {
|
||||
// U+0080 to U+07FF - two bytes
|
||||
// first of two bytes
|
||||
Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
|
||||
} else {
|
||||
if (!QChar::isSurrogate(u)) {
|
||||
// U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
|
||||
if (!Traits::allowNonCharacters &&
|
||||
QChar::isNonCharacter(u))
|
||||
return Traits::Error;
|
||||
// first of three bytes
|
||||
Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
|
||||
} else {
|
||||
// U+10000 to U+10FFFF - four bytes
|
||||
// need to get one extra codepoint
|
||||
if (Traits::availableUtf16(src, end) == 0)
|
||||
return Traits::EndOfString;
|
||||
ushort low = Traits::peekUtf16(src);
|
||||
if (!QChar::isHighSurrogate(u))
|
||||
return Traits::Error;
|
||||
if (!QChar::isLowSurrogate(low))
|
||||
return Traits::Error;
|
||||
Traits::advanceUtf16(src);
|
||||
uint ucs4 = QChar::surrogateToUcs4(u, low);
|
||||
if (!Traits::allowNonCharacters &&
|
||||
QChar::isNonCharacter(ucs4))
|
||||
return Traits::Error;
|
||||
// first byte
|
||||
Traits::appendByte(
|
||||
dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf)
|
||||
);
|
||||
// second of four bytes
|
||||
Traits::appendByte(
|
||||
dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f)
|
||||
);
|
||||
// for the rest of the bytes
|
||||
u = ushort(ucs4);
|
||||
}
|
||||
// second to last byte
|
||||
Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
|
||||
}
|
||||
// last byte
|
||||
Traits::appendByte(dst, 0x80 | (u & 0x3f));
|
||||
return 0;
|
||||
}
|
||||
inline bool isContinuationByte(uchar b)
|
||||
{
|
||||
return (b & 0xc0) == 0x80;
|
||||
}
|
||||
inline bool isContinuationByte(uchar b) { return (b & 0xc0) == 0x80; }
|
||||
/// returns the number of characters consumed (including \a b) in case of
|
||||
/// success; returns negative in case of error: Traits::Error or
|
||||
/// Traits::EndOfString
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end) {
|
||||
int charsNeeded;
|
||||
uint min_uc;
|
||||
uint uc;
|
||||
if (!Traits::skipAsciiHandling && b < 0x80) {
|
||||
// US-ASCII
|
||||
Traits::appendUtf16(dst, b);
|
||||
return 1;
|
||||
}
|
||||
if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) {
|
||||
// an UTF-8 first character must be at least 0xC0
|
||||
// however, all 0xC0 and 0xC1 first bytes can only produce overlong
|
||||
// sequences
|
||||
return Traits::Error;
|
||||
} else if (b < 0xe0) {
|
||||
charsNeeded = 2;
|
||||
min_uc = 0x80;
|
||||
uc = b & 0x1f;
|
||||
} else if (b < 0xf0) {
|
||||
charsNeeded = 3;
|
||||
min_uc = 0x800;
|
||||
uc = b & 0x0f;
|
||||
} else if (b < 0xf5) {
|
||||
charsNeeded = 4;
|
||||
min_uc = 0x10000;
|
||||
uc = b & 0x07;
|
||||
} else {
|
||||
// the last Unicode character is U+10FFFF
|
||||
// it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
|
||||
// therefore, a byte higher than 0xF4 is not the UTF-8 first byte
|
||||
return Traits::Error;
|
||||
}
|
||||
int bytesAvailable = Traits::availableBytes(src, end);
|
||||
if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) {
|
||||
// it's possible that we have an error instead of just unfinished bytes
|
||||
if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0)))
|
||||
return Traits::Error;
|
||||
if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1)))
|
||||
return Traits::Error;
|
||||
return Traits::EndOfString;
|
||||
}
|
||||
// first continuation character
|
||||
b = Traits::peekByte(src, 0);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 2) {
|
||||
// second continuation character
|
||||
b = Traits::peekByte(src, 1);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 3) {
|
||||
// third continuation character
|
||||
b = Traits::peekByte(src, 2);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
}
|
||||
}
|
||||
// we've decoded something; safety-check it
|
||||
if (!Traits::isTrusted) {
|
||||
if (uc < min_uc)
|
||||
return Traits::Error;
|
||||
if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
|
||||
return Traits::Error;
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
|
||||
return Traits::Error;
|
||||
}
|
||||
// write the UTF-16 sequence
|
||||
if (!QChar::requiresSurrogates(uc)) {
|
||||
// UTF-8 decoded and no surrogates are required
|
||||
// detach if necessary
|
||||
Traits::appendUtf16(dst, ushort(uc));
|
||||
} else {
|
||||
// UTF-8 decoded to something that requires a surrogate pair
|
||||
Traits::appendUcs4(dst, uc);
|
||||
}
|
||||
Traits::advanceByte(src, charsNeeded - 1);
|
||||
return charsNeeded;
|
||||
template<typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int fromUtf8(uchar b, OutputPtr& dst, InputPtr& src, InputPtr end)
|
||||
{
|
||||
int charsNeeded;
|
||||
uint min_uc;
|
||||
uint uc;
|
||||
if (!Traits::skipAsciiHandling && b < 0x80) {
|
||||
// US-ASCII
|
||||
Traits::appendUtf16(dst, b);
|
||||
return 1;
|
||||
}
|
||||
if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) {
|
||||
// an UTF-8 first character must be at least 0xC0
|
||||
// however, all 0xC0 and 0xC1 first bytes can only produce
|
||||
// overlong sequences
|
||||
return Traits::Error;
|
||||
} else if (b < 0xe0) {
|
||||
charsNeeded = 2;
|
||||
min_uc = 0x80;
|
||||
uc = b & 0x1f;
|
||||
} else if (b < 0xf0) {
|
||||
charsNeeded = 3;
|
||||
min_uc = 0x800;
|
||||
uc = b & 0x0f;
|
||||
} else if (b < 0xf5) {
|
||||
charsNeeded = 4;
|
||||
min_uc = 0x10000;
|
||||
uc = b & 0x07;
|
||||
} else {
|
||||
// the last Unicode character is U+10FFFF
|
||||
// it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
|
||||
// therefore, a byte higher than 0xF4 is not the UTF-8 first byte
|
||||
return Traits::Error;
|
||||
}
|
||||
int bytesAvailable = Traits::availableBytes(src, end);
|
||||
if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) {
|
||||
// it's possible that we have an error instead of just unfinished
|
||||
// bytes
|
||||
if (bytesAvailable > 0 &&
|
||||
!isContinuationByte(Traits::peekByte(src, 0)))
|
||||
return Traits::Error;
|
||||
if (bytesAvailable > 1 &&
|
||||
!isContinuationByte(Traits::peekByte(src, 1)))
|
||||
return Traits::Error;
|
||||
return Traits::EndOfString;
|
||||
}
|
||||
// first continuation character
|
||||
b = Traits::peekByte(src, 0);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 2) {
|
||||
// second continuation character
|
||||
b = Traits::peekByte(src, 1);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 3) {
|
||||
// third continuation character
|
||||
b = Traits::peekByte(src, 2);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
}
|
||||
}
|
||||
// we've decoded something; safety-check it
|
||||
if (!Traits::isTrusted) {
|
||||
if (uc < min_uc)
|
||||
return Traits::Error;
|
||||
if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
|
||||
return Traits::Error;
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
|
||||
return Traits::Error;
|
||||
}
|
||||
// write the UTF-16 sequence
|
||||
if (!QChar::requiresSurrogates(uc)) {
|
||||
// UTF-8 decoded and no surrogates are required
|
||||
// detach if necessary
|
||||
Traits::appendUtf16(dst, ushort(uc));
|
||||
} else {
|
||||
// UTF-8 decoded to something that requires a surrogate pair
|
||||
Traits::appendUcs4(dst, uc);
|
||||
}
|
||||
Traits::advanceByte(src, charsNeeded - 1);
|
||||
return charsNeeded;
|
||||
}
|
||||
} // namespace QUtf8Functions
|
||||
|
||||
struct QUtf8BaseTraits {
|
||||
static const bool isTrusted = false;
|
||||
static const bool allowNonCharacters = true;
|
||||
static const bool skipAsciiHandling = false;
|
||||
static const int Error = -1;
|
||||
static const int EndOfString = -2;
|
||||
static bool isValidCharacter(uint u) { return int(u) >= 0; }
|
||||
static void appendByte(uchar *&ptr, uchar b) { *ptr++ = b; }
|
||||
static uchar peekByte(const uchar *ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableBytes(const uchar *ptr, const uchar *end) {
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceByte(const uchar *&ptr, int n = 1) { ptr += n; }
|
||||
static void appendUtf16(ushort *&ptr, ushort uc) { *ptr++ = uc; }
|
||||
static void appendUcs4(ushort *&ptr, uint uc) {
|
||||
appendUtf16(ptr, QChar::highSurrogate(uc));
|
||||
appendUtf16(ptr, QChar::lowSurrogate(uc));
|
||||
}
|
||||
static ushort peekUtf16(const ushort *ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableUtf16(const ushort *ptr, const ushort *end) {
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceUtf16(const ushort *&ptr, int n = 1) { ptr += n; }
|
||||
// it's possible to output to UCS-4 too
|
||||
static void appendUtf16(uint *&ptr, ushort uc) { *ptr++ = uc; }
|
||||
static void appendUcs4(uint *&ptr, uint uc) { *ptr++ = uc; }
|
||||
static const bool isTrusted = false;
|
||||
static const bool allowNonCharacters = true;
|
||||
static const bool skipAsciiHandling = false;
|
||||
static const int error = -1;
|
||||
static const int endOfString = -2;
|
||||
static bool isValidCharacter(uint unsignedInt)
|
||||
{
|
||||
return static_cast<int>(unsignedInt) >= 0;
|
||||
}
|
||||
static void appendByte(uchar*& ptr, uchar byteVal) { *ptr++ = byteVal; }
|
||||
static uchar peekByte(const uchar* ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableBytes(const uchar* ptr, const uchar* end)
|
||||
{
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceByte(const uchar*& ptr, int n = 1) { ptr += n; }
|
||||
static void appendUtf16(ushort*& ptr, ushort unicodeChar)
|
||||
{
|
||||
*ptr++ = unicodeChar;
|
||||
}
|
||||
static void appendUcs4(ushort*& ptr, uint unicodeChar)
|
||||
{
|
||||
appendUtf16(ptr, QChar::highSurrogate(unicodeChar));
|
||||
appendUtf16(ptr, QChar::lowSurrogate(unicodeChar));
|
||||
}
|
||||
static ushort peekUtf16(const ushort* ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableUtf16(const ushort* ptr, const ushort* end)
|
||||
{
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceUtf16(const ushort*& ptr, int n = 1) { ptr += n; }
|
||||
// it's possible to output to UCS-4 too
|
||||
static void appendUtf16(uint*& ptr, ushort unicodeChar)
|
||||
{
|
||||
*ptr++ = unicodeChar;
|
||||
}
|
||||
static void appendUcs4(uint*& ptr, uint unicodeChar)
|
||||
{
|
||||
*ptr++ = unicodeChar;
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
||||
|
Loading…
Reference in New Issue
Block a user