aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAli Kianian <ali.kianian@qt.io>2024-02-02 14:33:52 +0200
committerAli Kianian <ali.kianian@qt.io>2024-02-05 15:15:11 +0000
commitc58efc4310d25bdbd62766f0efa087cbdafcd1f1 (patch)
treef69ccc2b82774afc709f6abd6038ccc1cf9e01bc
parentca9e72fe6cf9f8e0fe00d794bf7111abac372c54 (diff)
QmlDesigner: Implement new data store structure for Model Editor
Task-number: QDS-11778 Change-Id: Ia98fee976e5d81acc608b6209da270cbee2f9c61 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
-rw-r--r--share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml2
-rw-r--r--share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl82
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp589
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h60
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp320
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h9
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp257
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h11
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h2
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp13
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp82
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h10
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp15
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h8
15 files changed, 779 insertions, 685 deletions
diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml
index 3f8108005c..cb77d567e8 100644
--- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml
+++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml
@@ -192,7 +192,7 @@ StudioControls.Dialog {
enabled: root.fileExists && collectionName.text !== ""
onClicked: {
- let collectionImported = root.backendValue.importCollectionToDataStore(
+ let collectionImported = root.backendValue.importFile(
collectionName.text,
fileName.text)
diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl
index 8ebda6fb7e..ca9c173651 100644
--- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl
+++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl
@@ -1,30 +1,56 @@
{
- "book": [
- {
- "author": "Nigel Rees",
- "category": "reference",
- "price": 8.95,
- "title": "Sayings of the Century"
- },
- {
- "author": "Evelyn Waugh",
- "category": "fiction",
- "price": 12.99,
- "title": "Sword of Honor"
- },
- {
- "author": "Herman Melville",
- "category": "fiction",
- "isbn": "0-553-21311-3",
- "price": 8.99,
- "title": "Moby Dick"
- },
- {
- "author": "J. R. R. Tolkien",
- "category": "fiction",
- "isbn": "0-395-19395-8",
- "price": 22.99,
- "title": "The Lord of the Rings"
- }
- ]
+ "book": {
+ "columns": [
+ {
+ "name": "author",
+ "type": "String"
+ },
+ {
+ "name": "category",
+ "type": "String"
+ },
+ {
+ "name": "isbn",
+ "type": "String"
+ },
+ {
+ "name": "price",
+ "type": "Real"
+ },
+ {
+ "name": "title",
+ "type": "String"
+ }
+ ],
+ "data": [
+ [
+ "Nigel Rees",
+ "reference",
+ "",
+ 8.95,
+ "Sayings of the Century"
+ ],
+ [
+ "Evelyn Waugh",
+ "fiction",
+ "",
+ 12.99,
+ "Sword of Honor"
+ ],
+ [
+ "Herman Melville",
+ "fiction",
+ "0-553-21311-3",
+ 8.99,
+ "Moby Dick"
+ ],
+ [
+ "J. R. R. Tolkien",
+ "fiction",
+ "0-395-19395-8",
+ 22.99,
+ "The Lord of the Rings"
+ ]
+ ]
+ }
}
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
index f35e9f2ed2..f1827ee6be 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
@@ -3,16 +3,25 @@
#include "collectiondetails.h"
+#include "collectioneditorutils.h"
+
#include <utils/span.h>
#include <qqml.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
+#include <QTextStream>
#include <QUrl>
#include <QVariant>
namespace QmlDesigner {
+#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred")
+#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found")
+#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found")
+#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object")
+#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array")
+#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error")
struct CollectionProperty
{
@@ -28,30 +37,23 @@ const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings
class CollectionDetails::Private
{
- using SourceFormat = CollectionEditorConstants::SourceFormat;
-
public:
QList<CollectionProperty> properties;
- QList<QJsonObject> elements;
- SourceFormat sourceFormat = SourceFormat::Unknown;
+ QList<QJsonArray> dataRecords;
CollectionReference reference;
bool isChanged = false;
bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); }
- bool isValidRowId(int row) const { return row > -1 && row < elements.size(); }
+ bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); }
};
inline static bool isValidColorName(const QString &colorName)
{
-#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
- return QColor::isValidColorName(colorName);
-#else
constexpr QStringView colorPattern(
u"(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)");
static const QRegularExpression colorRegex(colorPattern.toString());
return colorRegex.match(colorName).hasMatch();
-#endif // >= Qt 6.4
}
/**
@@ -115,7 +117,7 @@ inline static bool getCustomUrl(const QString &value,
return false;
}
-static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonValue &value)
+static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value)
{
using DataType = CollectionDetails::DataType;
using JsonType = QJsonValue::Type;
@@ -133,6 +135,10 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV
}
case JsonType::String: {
const QString stringValue = value.toString();
+
+ if (stringValue.isEmpty())
+ return DataType::Unknown;
+
if (isValidColorName(stringValue))
return DataType::Color;
@@ -147,6 +153,42 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV
}
}
+static QList<CollectionProperty> getColumnsFromImportedJsonArray(const QJsonArray &importedArray)
+{
+ using DataType = CollectionDetails::DataType;
+
+ QHash<QString, int> resultSet;
+ QList<CollectionProperty> result;
+
+ for (const QJsonValue &value : importedArray) {
+ if (value.isObject()) {
+ const QJsonObject object = value.toObject();
+ QJsonObject::ConstIterator element = object.constBegin();
+ const QJsonObject::ConstIterator stopItem = object.constEnd();
+
+ while (element != stopItem) {
+ const QString propertyName = element.key();
+ if (resultSet.contains(propertyName)) {
+ CollectionProperty &property = result[resultSet.value(propertyName)];
+ if (property.type == DataType::Unknown) {
+ property.type = dataTypeFromJsonValue(element.value());
+ } else if (property.type == DataType::Integer) {
+ const DataType currentCellDataType = dataTypeFromJsonValue(element.value());
+ if (currentCellDataType == DataType::Real)
+ property.type = currentCellDataType;
+ }
+ } else {
+ result.append({propertyName, dataTypeFromJsonValue(element.value())});
+ resultSet.insert(propertyName, resultSet.size());
+ }
+ ++element;
+ }
+ }
+ }
+
+ return result;
+}
+
static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type)
{
using DataType = CollectionDetails::DataType;
@@ -214,6 +256,30 @@ static QJsonValue variantToJsonValue(
}
}
+inline static bool isEmptyJsonValue(const QJsonValue &value)
+{
+ return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty());
+}
+
+QString CollectionParseError::errorString() const
+{
+ switch (errorNo) {
+ case NoError:
+ return COLLERR_OK;
+ case MainObjectMissing:
+ return COLLERR_MAINOBJECT;
+ case CollectionNameNotFound:
+ return COLLERR_COLLECTIONNAME;
+ case CollectionIsNotObject:
+ return COLLERR_COLLECTIONOBJ;
+ case ColumnsBlockIsNotArray:
+ return COLLERR_COLUMNARRAY;
+ case UnknownError:
+ default:
+ return COLLERR_UNKNOWN;
+ }
+}
+
CollectionDetails::CollectionDetails()
: d(new Private())
{}
@@ -224,134 +290,102 @@ CollectionDetails::CollectionDetails(const CollectionReference &reference)
d->reference = reference;
}
-CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
-
-CollectionDetails::~CollectionDetails() = default;
-
-void CollectionDetails::resetDetails(const QStringList &propertyNames,
- const QList<QJsonObject> &elements,
- CollectionEditorConstants::SourceFormat format)
+void CollectionDetails::resetData(const QJsonDocument &localDocument,
+ const QString &collectionToImport,
+ CollectionParseError *error)
{
- if (!isValid())
- return;
-
- d->properties = Utils::transform(propertyNames, [](const QString &name) -> CollectionProperty {
- return {name, DataType::Unknown};
- });
+ CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error);
+ d->properties.swap(importedCollection.d->properties);
+ d->dataRecords.swap(importedCollection.d->dataRecords);
+}
- d->elements = elements;
- d->sourceFormat = format;
+CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
- resetPropertyTypes();
- markSaved();
-}
+CollectionDetails::~CollectionDetails() = default;
void CollectionDetails::insertColumn(const QString &propertyName,
int colIdx,
const QVariant &defaultValue,
DataType type)
{
- if (!isValid())
- return;
-
if (containsPropertyName(propertyName))
return;
CollectionProperty property = {propertyName, type};
- if (d->isValidColumnId(colIdx))
+ if (d->isValidColumnId(colIdx)) {
d->properties.insert(colIdx, property);
- else
+ } else {
+ colIdx = d->properties.size();
d->properties.append(property);
+ }
- QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
- for (QJsonObject &element : d->elements)
- element.insert(propertyName, defaultJsonValue);
+ const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
+ for (QJsonArray &record : d->dataRecords)
+ record.insert(colIdx, defaultJsonValue);
markChanged();
}
bool CollectionDetails::removeColumns(int colIdx, int count)
{
- if (count < 1 || !isValid() || !d->isValidColumnId(colIdx))
+ if (!d->isValidColumnId(colIdx))
return false;
int maxCount = d->properties.count() - colIdx;
count = std::min(maxCount, count);
- const QList<CollectionProperty> removedProperties = d->properties.mid(colIdx, count);
+ if (count < 1)
+ return false;
+
d->properties.remove(colIdx, count);
- for (const CollectionProperty &property : removedProperties) {
- for (QJsonObject &element : d->elements)
- element.remove(property.name);
- }
+ for (QJsonArray &record : d->dataRecords) {
+ QJsonArray newElement;
- markChanged();
+ auto elementItr = record.constBegin();
+ auto elementEnd = elementItr + colIdx;
+ while (elementItr != elementEnd)
+ newElement.append(*(elementItr++));
- return true;
-}
+ elementItr += count;
+ elementEnd = record.constEnd();
-void CollectionDetails::insertElementAt(std::optional<QJsonObject> object, int row)
-{
- if (!isValid())
- return;
-
- auto insertJson = [this, row](const QJsonObject &jsonObject) {
- if (d->isValidRowId(row))
- d->elements.insert(row, jsonObject);
- else
- d->elements.append(jsonObject);
- };
+ while (elementItr != elementEnd)
+ newElement.append(*(elementItr++));
- if (object.has_value()) {
- insertJson(object.value());
- } else {
- QJsonObject defaultObject;
- for (const CollectionProperty &property : std::as_const(d->properties))
- defaultObject.insert(property.name, {});
- insertJson(defaultObject);
+ record = newElement;
}
markChanged();
+
+ return true;
}
-void CollectionDetails::insertEmptyElements(int row, int count)
+void CollectionDetails::insertEmptyRows(int row, int count)
{
- if (!isValid())
- return;
-
if (count < 1)
return;
row = qBound(0, row, rows());
- d->elements.insert(row, count, {});
+
+ insertRecords({}, row, count);
markChanged();
}
-bool CollectionDetails::removeElements(int row, int count)
+bool CollectionDetails::removeRows(int row, int count)
{
- if (count < 1 || !isValid() || !d->isValidRowId(row))
+ if (!d->isValidRowId(row))
return false;
- int maxCount = d->elements.count() - row;
+ int maxCount = d->dataRecords.count() - row;
count = std::min(maxCount, count);
- QSet<QString> removedProperties;
- Utils::span elementsSpan{std::as_const(d->elements)};
- for (const QJsonObject &element : elementsSpan.subspan(row, count)) {
- const QStringList elementPropertyNames = element.keys();
- for (const QString &removedProperty : elementPropertyNames)
- removedProperties.insert(removedProperty);
- }
-
- d->elements.remove(row, count);
-
- for (const QString &removedProperty : removedProperties)
- resetPropertyType(removedProperty);
+ if (count < 1)
+ return false;
+ d->dataRecords.remove(row, count);
markChanged();
-
return true;
}
@@ -360,13 +394,12 @@ bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &va
if (!d->isValidRowId(row) || !d->isValidColumnId(column))
return false;
- QJsonObject &element = d->elements[row];
QVariant currentValue = data(row, column);
-
if (value == currentValue)
return false;
- element.insert(d->properties.at(column).name, variantToJsonValue(value, typeAt(column)));
+ QJsonArray &record = d->dataRecords[row];
+ record.replace(column, variantToJsonValue(value, typeAt(column)));
markChanged();
return true;
}
@@ -377,17 +410,10 @@ bool CollectionDetails::setPropertyName(int column, const QString &value)
return false;
const CollectionProperty &oldProperty = d->properties.at(column);
- const QString oldColumnName = oldProperty.name;
- if (oldColumnName == value)
+ if (oldProperty.name == value)
return false;
d->properties.replace(column, {value, oldProperty.type});
- for (QJsonObject &element : d->elements) {
- if (element.contains(oldColumnName)) {
- element.insert(value, element.value(oldColumnName));
- element.remove(oldColumnName);
- }
- }
markChanged();
return true;
@@ -395,7 +421,7 @@ bool CollectionDetails::setPropertyName(int column, const QString &value)
bool CollectionDetails::setPropertyType(int column, DataType type)
{
- if (!isValid() || !d->isValidColumnId(column))
+ if (!d->isValidColumnId(column))
return false;
bool changed = false;
@@ -406,12 +432,12 @@ bool CollectionDetails::setPropertyType(int column, DataType type)
const DataType formerType = property.type;
property.type = type;
- for (QJsonObject &element : d->elements) {
- if (element.contains(property.name)) {
- const QJsonValue value = element.value(property.name);
+ for (QJsonArray &rowData : d->dataRecords) {
+ if (column < rowData.size()) {
+ const QJsonValue value = rowData.at(column);
const QVariant properTypedValue = valueToVariant(value, formerType);
const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type);
- element.insert(property.name, properTypedJsonValue);
+ rowData.replace(column, properTypedJsonValue);
changed = true;
}
}
@@ -427,29 +453,15 @@ CollectionReference CollectionDetails::reference() const
return d->reference;
}
-CollectionEditorConstants::SourceFormat CollectionDetails::sourceFormat() const
-{
- return d->sourceFormat;
-}
-
QVariant CollectionDetails::data(int row, int column) const
{
- if (!isValid())
- return {};
-
if (!d->isValidRowId(row))
return {};
if (!d->isValidColumnId(column))
return {};
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &elementNode = d->elements.at(row);
-
- if (!elementNode.contains(propertyName))
- return {};
-
- const QJsonValue cellValue = elementNode.value(propertyName);
+ const QJsonValue cellValue = d->dataRecords.at(row).at(column);
if (typeAt(column) == DataType::Image) {
const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl();
@@ -482,48 +494,37 @@ CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const
if (!d->isValidRowId(row) || !d->isValidColumnId(column))
return {};
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &element = d->elements.at(row);
-
- if (element.contains(propertyName))
- return collectionDataTypeFromJsonValue(element.value(propertyName));
-
- return {};
+ const QJsonValue cellData = d->dataRecords.at(row).at(column);
+ return dataTypeFromJsonValue(cellData);
}
DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const
{
- const QString &propertyName = d->properties.at(column).name;
- const QJsonObject &element = d->elements.at(row);
+ const QJsonValue cellValue = d->dataRecords.at(row).at(column);
const DataType columnType = typeAt(column);
const DataType cellType = typeAt(row, column);
- if (columnType == DataType::Unknown || element.isEmpty()
- || data(row, column) == QVariant::fromValue(nullptr)) {
+
+ if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue))
return DataTypeWarning::Warning::None;
- }
- if (element.contains(propertyName)) {
- if (columnType == DataType::Real && cellType == DataType::Integer)
- return DataTypeWarning::Warning::None;
- else if (columnType != cellType)
- return DataTypeWarning::Warning::CellDataTypeMismatch;
- }
+ if (columnType == DataType::Real && cellType == DataType::Integer)
+ return DataTypeWarning::Warning::None;
+
+ if (columnType != cellType)
+ return DataTypeWarning::Warning::CellDataTypeMismatch;
return DataTypeWarning::Warning::None;
}
-bool CollectionDetails::containsPropertyName(const QString &propertyName)
+bool CollectionDetails::containsPropertyName(const QString &propertyName) const
{
- if (!isValid())
- return false;
-
return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) {
return property.name == propertyName;
});
}
-bool CollectionDetails::isValid() const
+bool CollectionDetails::hasValidReference() const
{
return d->reference.node.isValid() && d->reference.name.size();
}
@@ -540,7 +541,7 @@ int CollectionDetails::columns() const
int CollectionDetails::rows() const
{
- return d->elements.size();
+ return d->dataRecords.size();
}
bool CollectionDetails::markSaved()
@@ -565,6 +566,90 @@ void CollectionDetails::resetReference(const CollectionReference &reference)
}
}
+QString CollectionDetails::toJson() const
+{
+ QJsonArray exportedArray;
+ const int propertyCount = d->properties.count();
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords)) {
+ const int valueCount = std::min(int(record.count()), propertyCount);
+
+ QJsonObject exportedElement;
+ for (int i = 0; i < valueCount; ++i) {
+ const QJsonValue &value = record.at(i);
+ if (!isEmptyJsonValue(value))
+ exportedElement.insert(d->properties.at(i).name, value);
+ }
+
+ exportedArray.append(exportedElement);
+ }
+
+ return QString::fromUtf8(QJsonDocument(exportedArray).toJson());
+}
+
+QString CollectionDetails::toCsv() const
+{
+ QString content;
+
+ auto gotoNextLine = [&content]() {
+ if (content.size() && content.back() == ',')
+ content.back() = '\n';
+ else
+ content += "\n";
+ };
+
+ const int propertyCount = d->properties.count();
+ if (propertyCount <= 0)
+ return "";
+
+ for (const CollectionProperty &property : std::as_const(d->properties))
+ content += property.name + ',';
+
+ gotoNextLine();
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords)) {
+ const int valueCount = std::min(int(record.count()), propertyCount);
+ int i = 0;
+ for (; i < valueCount; ++i) {
+ const QJsonValue &value = record.at(i);
+
+ if (value.isDouble())
+ content += QString::number(value.toDouble()) + ',';
+ else
+ content += value.toString() + ',';
+ }
+
+ for (; i < propertyCount; ++i)
+ content += ',';
+
+ gotoNextLine();
+ }
+
+ return content;
+}
+
+QJsonObject CollectionDetails::toLocalJson() const
+{
+ QJsonObject collectionObject;
+ QJsonArray columnsArray;
+ QJsonArray dataArray;
+
+ for (const CollectionProperty &property : std::as_const(d->properties)) {
+ QJsonObject columnObject;
+ columnObject.insert("name", property.name);
+ columnObject.insert("type", CollectionEditorUtils::dataTypeToString(property.type));
+ columnsArray.append(columnObject);
+ }
+
+ for (const QJsonArray &record : std::as_const(d->dataRecords))
+ dataArray.append(record);
+
+ collectionObject.insert("columns", columnsArray);
+ collectionObject.insert("data", dataArray);
+
+ return collectionObject;
+}
+
void CollectionDetails::registerDeclarativeType()
{
typedef CollectionDetails::DataType DataType;
@@ -575,93 +660,209 @@ void CollectionDetails::registerDeclarativeType()
qmlRegisterUncreatableType<DataTypeWarning>("CollectionDetails", 1, 0, "Warning", "Enum type");
}
-CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
+CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document)
{
- CollectionDetails value(other);
- swap(value);
- return *this;
-}
+ QStringList headers;
+ QJsonArray importedArray;
-void CollectionDetails::markChanged()
-{
- d->isChanged = true;
-}
+ QTextStream stream(document);
-void CollectionDetails::resetPropertyType(const QString &propertyName)
-{
- for (CollectionProperty &property : d->properties) {
- if (property.name == propertyName)
- resetPropertyType(property);
+ if (!stream.atEnd())
+ headers = stream.readLine().split(',');
+
+ for (QString &header : headers)
+ header = header.trimmed();
+
+ if (!headers.isEmpty()) {
+ while (!stream.atEnd()) {
+ const QStringList recordDataList = stream.readLine().split(',');
+ int column = -1;
+ QJsonObject recordData;
+ for (const QString &cellData : recordDataList) {
+ if (++column == headers.size())
+ break;
+ recordData.insert(headers.at(column), cellData);
+ }
+ importedArray.append(recordData);
+ }
}
+
+ return fromImportedJson(importedArray);
}
-void CollectionDetails::resetPropertyType(CollectionProperty &property)
+CollectionDetails CollectionDetails::fromImportedJson(const QJsonDocument &document)
{
- const QString &propertyName = property.name;
- DataType columnType = DataType::Unknown;
- for (const QJsonObject &element : std::as_const(d->elements)) {
- if (element.contains(propertyName)) {
- const DataType cellType = collectionDataTypeFromJsonValue(element.value(propertyName));
- if (cellType != DataType::Unknown) {
- if (columnType == DataType::Integer && cellType != DataType::Real)
- continue;
-
- columnType = cellType;
- if (columnType == DataType::Integer)
- continue;
+ QJsonArray importedCollection;
+ auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
+ QJsonArray resultArray;
+ for (const QJsonValue &collectionData : array) {
+ if (collectionData.isObject()) {
+ QJsonObject rowObject = collectionData.toObject();
+ const QStringList rowKeys = rowObject.keys();
+ for (const QString &key : rowKeys) {
+ const QJsonValue cellValue = rowObject.value(key);
+ if (cellValue.isArray())
+ rowObject.remove(key);
+ }
+ resultArray.push_back(rowObject);
+ }
+ }
+ return resultArray;
+ };
+ if (document.isArray()) {
+ importedCollection = refineJsonArray(document.array());
+ } else if (document.isObject()) {
+ QJsonObject documentObject = document.object();
+ const QStringList mainKeys = documentObject.keys();
+
+ bool arrayFound = false;
+ for (const QString &key : mainKeys) {
+ const QJsonValue value = documentObject.value(key);
+ if (value.isArray()) {
+ arrayFound = true;
+ importedCollection = refineJsonArray(value.toArray());
break;
}
}
+
+ if (!arrayFound) {
+ QJsonObject singleObject;
+ for (const QString &key : mainKeys) {
+ const QJsonValue value = documentObject.value(key);
+
+ if (!value.isObject())
+ singleObject.insert(key, value);
+ }
+ importedCollection.push_back(singleObject);
+ }
}
- property.type = columnType;
+
+ return fromImportedJson(importedCollection);
}
-void CollectionDetails::resetPropertyTypes()
+CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document,
+ const QString &collectionName,
+ CollectionParseError *error)
{
- for (CollectionProperty &property : d->properties)
- resetPropertyType(property);
+ auto setError = [&error](CollectionParseError::ParseError parseError) {
+ if (error)
+ error->errorNo = parseError;
+ };
+
+ setError(CollectionParseError::NoError);
+
+ if (document.isObject()) {
+ QJsonObject collectionMap = document.object();
+ if (collectionMap.contains(collectionName)) {
+ QJsonValue collectionValue = collectionMap.value(collectionName);
+ if (collectionValue.isObject())
+ return fromLocalCollection(collectionValue.toObject());
+ else
+ setError(CollectionParseError::CollectionIsNotObject);
+ } else {
+ setError(CollectionParseError::CollectionNameNotFound);
+ }
+ } else {
+ setError(CollectionParseError::MainObjectMissing);
+ }
+
+ return CollectionDetails{};
}
-QJsonArray CollectionDetails::getCollectionAsJsonArray() const
+CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
{
- QJsonArray collectionArray;
-
- for (const QJsonObject &element : std::as_const(d->elements))
- collectionArray.push_back(element);
+ CollectionDetails value(other);
+ swap(value);
+ return *this;
+}
- return collectionArray;
+void CollectionDetails::markChanged()
+{
+ d->isChanged = true;
}
-QString CollectionDetails::getCollectionAsJsonString() const
+void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count)
{
- return QString::fromUtf8(QJsonDocument(getCollectionAsJsonArray()).toJson());
+ if (count < 1)
+ return;
+
+ QJsonArray localRecord;
+ const int columnsCount = columns();
+ for (int i = 0; i < columnsCount; i++) {
+ const QJsonValue originalCellData = record.at(i);
+ if (originalCellData.isArray())
+ localRecord.append({});
+ else
+ localRecord.append(originalCellData);
+ }
+
+ if (idx > d->dataRecords.size() || idx < 0)
+ idx = d->dataRecords.size();
+
+ d->dataRecords.insert(idx, count, localRecord);
}
-QString CollectionDetails::getCollectionAsCsvString() const
+CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray)
{
- QString content;
- if (d->properties.count() <= 0)
- return "";
+ const QList<CollectionProperty> columnData = getColumnsFromImportedJsonArray(importedArray);
+ QList<QJsonArray> localJsonArray;
+ for (const QJsonValue &importedRowValue : importedArray) {
+ QJsonObject importedRowObject = importedRowValue.toObject();
+ QJsonArray localRow;
+ for (const CollectionProperty &property : columnData)
+ localRow.append(importedRowObject.value(property.name));
+ localJsonArray.append(localRow);
+ }
+ CollectionDetails result;
+ result.d->properties = columnData;
+ result.d->dataRecords = localJsonArray;
+ result.markSaved();
- for (const CollectionProperty &property : std::as_const(d->properties))
- content += property.name + ',';
+ return result;
+}
- content.back() = '\n';
+CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection,
+ CollectionParseError *error)
+{
+ auto setError = [&error](CollectionParseError::ParseError parseError) {
+ if (error)
+ error->errorNo = parseError;
+ };
- for (const QJsonObject &elementsRow : std::as_const(d->elements)) {
- for (const CollectionProperty &property : std::as_const(d->properties)) {
- const QJsonValue &value = elementsRow.value(property.name);
+ CollectionDetails result;
+ setError(CollectionParseError::NoError);
+
+ if (localCollection.contains("columns")) {
+ const QJsonValue columnsValue = localCollection.value("columns");
+ if (columnsValue.isArray()) {
+ const QJsonArray columns = columnsValue.toArray();
+ for (const QJsonValue &columnValue : columns) {
+ if (columnValue.isObject()) {
+ const QJsonObject column = columnValue.toObject();
+ const QString columnName = column.value("name").toString();
+ if (!columnName.isEmpty()) {
+ result.insertColumn(columnName,
+ -1,
+ {},
+ CollectionEditorUtils::dataTypeFromString(
+ column.value("type").toString()));
+ }
+ }
+ }
- if (value.isDouble())
- content += QString::number(value.toDouble()) + ',';
- else
- content += value.toString() + ',';
+ if (int columnsCount = result.columns()) {
+ const QJsonArray dataRecords = localCollection.value("data").toArray();
+ for (const QJsonValue &dataRecordValue : dataRecords)
+ result.insertRecords(dataRecordValue.toArray());
+ }
+ } else {
+ setError(CollectionParseError::ColumnsBlockIsNotArray);
+ return result;
}
- content.back() = '\n';
}
- return content;
+ return result;
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
index a18c557c52..2ccc69e447 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
@@ -3,7 +3,6 @@
#pragma once
-#include "collectioneditorconstants.h"
#include "modelnode.h"
#include <QSharedPointer>
@@ -36,8 +35,6 @@ struct CollectionReference
struct CollectionProperty;
struct DataTypeWarning {
- Q_GADGET
-
public:
enum Warning { None, CellDataTypeMismatch };
Q_ENUM(Warning)
@@ -47,14 +44,31 @@ public:
: warning(warning)
{}
- static QString getDataTypeWarningString(Warning warning) {
+ static QString getDataTypeWarningString(Warning warning)
+ {
return dataTypeWarnings.value(warning);
}
private:
+ Q_GADGET
static const QMap<Warning, QString> dataTypeWarnings;
};
+struct CollectionParseError
+{
+ enum ParseError {
+ NoError,
+ MainObjectMissing,
+ CollectionNameNotFound,
+ CollectionIsNotObject,
+ ColumnsBlockIsNotArray,
+ UnknownError
+ };
+
+ ParseError errorNo = ParseError::NoError;
+ QString errorString() const;
+};
+
class CollectionDetails
{
Q_GADGET
@@ -68,33 +82,32 @@ public:
CollectionDetails(const CollectionDetails &other);
~CollectionDetails();
- void resetDetails(const QStringList &propertyNames,
- const QList<QJsonObject> &elements,
- CollectionEditorConstants::SourceFormat format);
+ void resetData(const QJsonDocument &localDocument,
+ const QString &collectionToImport,
+ CollectionParseError *error = nullptr);
+
void insertColumn(const QString &propertyName,
int colIdx = -1,
const QVariant &defaultValue = {},
DataType type = DataType::Unknown);
bool removeColumns(int colIdx, int count = 1);
- void insertElementAt(std::optional<QJsonObject> object, int row = -1);
- void insertEmptyElements(int row = 0, int count = 1);
- bool removeElements(int row, int count = 1);
+ void insertEmptyRows(int row = 0, int count = 1);
+ bool removeRows(int row, int count = 1);
bool setPropertyValue(int row, int column, const QVariant &value);
bool setPropertyName(int column, const QString &value);
bool setPropertyType(int column, DataType type);
CollectionReference reference() const;
- CollectionEditorConstants::SourceFormat sourceFormat() const;
QVariant data(int row, int column) const;
QString propertyAt(int column) const;
DataType typeAt(int column) const;
DataType typeAt(int row, int column) const;
DataTypeWarning::Warning cellWarningCheck(int row, int column) const;
- bool containsPropertyName(const QString &propertyName);
+ bool containsPropertyName(const QString &propertyName) const;
- bool isValid() const;
+ bool hasValidReference() const;
bool isChanged() const;
int columns() const;
@@ -104,23 +117,32 @@ public:
void swap(CollectionDetails &other);
void resetReference(const CollectionReference &reference);
- QString getCollectionAsJsonString() const;
- QString getCollectionAsCsvString() const;
- QJsonArray getCollectionAsJsonArray() const;
+ QString toJson() const;
+ QString toCsv() const;
+ QJsonObject toLocalJson() const;
static void registerDeclarativeType();
+ static CollectionDetails fromImportedCsv(const QByteArray &document);
+ static CollectionDetails fromImportedJson(const QJsonDocument &document);
+ static CollectionDetails fromLocalJson(const QJsonDocument &document,
+ const QString &collectionName,
+ CollectionParseError *error = nullptr);
+
CollectionDetails &operator=(const CollectionDetails &other);
private:
void markChanged();
- void resetPropertyType(const QString &propertyName);
- void resetPropertyType(CollectionProperty &property);
- void resetPropertyTypes();
+ void insertRecords(const QJsonArray &record, int idx = -1, int count = 1);
+
+ static CollectionDetails fromImportedJson(const QJsonArray &importedArray);
+ static CollectionDetails fromLocalCollection(const QJsonObject &localCollection,
+ CollectionParseError *error = nullptr);
// The private data is supposed to be shared between the copies
class Private;
QSharedPointer<Private> d;
};
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
index 51e3be9ad6..17a26c58c5 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
@@ -3,7 +3,6 @@
#include "collectiondetailsmodel.h"
-#include "collectioneditorconstants.h"
#include "collectioneditorutils.h"
#include "modelnode.h"
@@ -13,116 +12,11 @@
#include <utils/qtcassert.h>
#include <utils/textfileformat.h>
-#include <QFile>
-#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
-namespace {
-
-QStringList getJsonHeaders(const QJsonArray &collectionArray)
-{
- QSet<QString> resultSet;
- QList<QString> result;
-
- for (const QJsonValue &value : collectionArray) {
- if (value.isObject()) {
- const QJsonObject object = value.toObject();
- QJsonObject::ConstIterator element = object.constBegin();
- const QJsonObject::ConstIterator stopItem = object.constEnd();
-
- while (element != stopItem) {
- const QString property = element.key();
- if (!resultSet.contains(property)) {
- result.append(property);
- resultSet.insert(property);
- }
- ++element;
- }
- }
- }
-
- return result;
-}
-
-class CollectionDataTypeHelper
-{
-public:
- using DataType = QmlDesigner::CollectionDetails::DataType;
-
- static QString typeToString(DataType dataType)
- {
- static const QHash<DataType, QString> typeStringHash = typeToStringHash();
- return typeStringHash.value(dataType);
- }
-
- static DataType typeFromString(const QString &dataType)
- {
- static const QHash<QString, DataType> stringTypeHash = stringToTypeHash();
- return stringTypeHash.value(dataType, DataType::Unknown);
- }
-
- static QStringList typesStringList()
- {
- static const QStringList typesList = orderedTypeNames();
- return typesList;
- }
-
-private:
- CollectionDataTypeHelper() = delete;
-
- static QHash<DataType, QString> typeToStringHash()
- {
- return {
- {DataType::Unknown, "Unknown"},
- {DataType::String, "String"},
- {DataType::Url, "Url"},
- {DataType::Real, "Real"},
- {DataType::Integer, "Integer"},
- {DataType::Boolean, "Boolean"},
- {DataType::Image, "Image"},
- {DataType::Color, "Color"},
- };
- }
-
- static QHash<QString, DataType> stringToTypeHash()
- {
- QHash<QString, DataType> stringTypeHash;
- const QHash<DataType, QString> typeStringHash = typeToStringHash();
- for (const auto &transferItem : typeStringHash.asKeyValueRange())
- stringTypeHash.insert(transferItem.second, transferItem.first);
-
- return stringTypeHash;
- }
-
- static QStringList orderedTypeNames()
- {
- const QList<DataType> orderedtypes{
- DataType::String,
- DataType::Integer,
- DataType::Real,
- DataType::Image,
- DataType::Color,
- DataType::Url,
- DataType::Boolean,
- DataType::Unknown,
- };
-
- QStringList orderedNames;
- QHash<DataType, QString> typeStringHash = typeToStringHash();
-
- for (const DataType &type : orderedtypes)
- orderedNames.append(typeStringHash.take(type));
-
- Q_ASSERT(typeStringHash.isEmpty());
- return orderedNames;
- }
-};
-
-} // namespace
-
namespace QmlDesigner {
CollectionDetailsModel::CollectionDetailsModel(QObject *parent)
@@ -161,6 +55,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return {};
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
if (role == SelectedRole)
return (index.column() == m_selectedColumn || index.row() == m_selectedRow);
@@ -181,6 +77,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (!index.isValid())
return {};
@@ -208,6 +106,8 @@ bool CollectionDetailsModel::setHeaderData(int section,
const QVariant &value,
[[maybe_unused]] int role)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (orientation == Qt::Vertical)
return false;
@@ -220,13 +120,15 @@ bool CollectionDetailsModel::setHeaderData(int section,
bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (count < 1)
return false;
row = qBound(0, row, rowCount());
beginResetModel();
- m_currentCollection.insertEmptyElements(row, count);
+ m_currentCollection.insertEmptyRows(row, count);
endResetModel();
selectRow(row);
@@ -235,6 +137,8 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con
bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (column < 0 || column >= columnCount(parent) || count < 1)
return false;
@@ -258,12 +162,14 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn
bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (row < 0 || row >= rowCount(parent) || count < 1)
return false;
count = std::min(count, rowCount(parent) - row);
beginRemoveRows(parent, row, row + count - 1);
- bool rowsRemoved = m_currentCollection.removeElements(row, count);
+ bool rowsRemoved = m_currentCollection.removeRows(row, count);
endRemoveRows();
ensureSingleCell();
@@ -282,7 +188,7 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta
{
if (orientation == Qt::Horizontal) {
if (role == DataTypeRole)
- return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(section));
+ return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(section));
else
return m_currentCollection.propertyAt(section);
}
@@ -295,6 +201,8 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta
CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::Unknown);
+
return m_currentCollection.typeAt(column);
}
@@ -310,21 +218,29 @@ int CollectionDetailsModel::selectedRow() const
QString CollectionDetailsModel::propertyName(int column) const
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
return m_currentCollection.propertyAt(column);
}
QString CollectionDetailsModel::propertyType(int column) const
{
- return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(column));
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
+
+ return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(column));
}
bool CollectionDetailsModel::isPropertyAvailable(const QString &name)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
return m_currentCollection.containsPropertyName(name);
}
bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
if (m_currentCollection.containsPropertyName(name))
return false;
@@ -335,7 +251,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS
m_currentCollection.insertColumn(name,
column,
{},
- CollectionDataTypeHelper::typeFromString(propertyType));
+ CollectionEditorUtils::dataTypeFromString(propertyType));
endInsertColumns();
return m_currentCollection.containsPropertyName(name);
}
@@ -379,8 +295,10 @@ bool CollectionDetailsModel::renameColumn(int section, const QString &newValue)
bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue)
{
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
+
bool changed = m_currentCollection.setPropertyType(column,
- CollectionDataTypeHelper::typeFromString(
+ CollectionEditorUtils::dataTypeFromString(
newValue));
if (changed) {
emit headerDataChanged(Qt::Horizontal, column, column);
@@ -430,7 +348,7 @@ void CollectionDetailsModel::deselectAll()
QStringList CollectionDetailsModel::typesList()
{
- return CollectionDataTypeHelper::typesStringList();
+ return CollectionEditorUtils::dataTypesStringList();
}
void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection)
@@ -451,10 +369,7 @@ void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const Q
} else {
deselectAll();
switchToCollection(newReference);
- if (sourceNode.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- loadJsonCollection(fileName, collection);
- else if (sourceNode.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME)
- loadCsvCollection(fileName, collection);
+ loadJsonCollection(fileName, collection);
}
}
@@ -516,7 +431,7 @@ bool CollectionDetailsModel::saveDataStoreCollections()
for (CollectionDetails &openedCollection : m_openedCollections) {
const CollectionReference reference = openedCollection.reference();
if (reference.node == node) {
- obj.insert(reference.name, openedCollection.getCollectionAsJsonArray());
+ obj.insert(reference.name, openedCollection.toLocalJson());
collectionsToBeSaved << openedCollection;
}
}
@@ -545,14 +460,14 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url)
using Utils::FilePath;
using Utils::TextFileFormat;
- QTC_ASSERT(m_currentCollection.isValid(), return false);
+ QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
bool saved = false;
const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
: url.toString());
const QString saveFormat = filePath.toFileInfo().suffix().toLower();
- const QString content = saveFormat == "csv" ? m_currentCollection.getCollectionAsCsvString()
- : m_currentCollection.getCollectionAsJsonString();
+ const QString content = saveFormat == "csv" ? m_currentCollection.toCsv()
+ : m_currentCollection.toJson();
TextFileFormat textFileFormat;
textFileFormat.codec = EditorManager::defaultTextCodec();
@@ -566,6 +481,49 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url)
return saved;
}
+const CollectionDetails CollectionDetailsModel::upToDateConstCollection(
+ const CollectionReference &reference) const
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ CollectionDetails collection;
+
+ if (m_openedCollections.contains(reference)) {
+ collection = m_openedCollections.value(reference);
+ } else {
+ QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node);
+ FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
+ : url.toString());
+ FileReader file;
+
+ if (!file.fetch(path))
+ return collection;
+
+ QJsonParseError jpe;
+ QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
+
+ if (jpe.error != QJsonParseError::NoError)
+ return collection;
+
+ collection = CollectionDetails::fromLocalJson(document, reference.name);
+ collection.resetReference(reference);
+ }
+ return collection;
+}
+
+bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference,
+ const QString &columnName) const
+{
+ const CollectionDetails collection = upToDateConstCollection(reference);
+ return collection.containsPropertyName(columnName);
+}
+
+QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const
+{
+ const CollectionDetails collection = upToDateConstCollection(reference);
+ return collection.propertyAt(0);
+}
+
void CollectionDetailsModel::updateEmpty()
{
bool isEmptyNow = rowCount() == 0;
@@ -603,113 +561,25 @@ void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &c
void CollectionDetailsModel::closeCurrentCollectionIfSaved()
{
- if (m_currentCollection.isValid()) {
+ if (m_currentCollection.hasValidReference()) {
closeCollectionIfSaved(m_currentCollection.reference());
m_currentCollection = CollectionDetails{};
}
}
-void CollectionDetailsModel::loadJsonCollection(const QString &source, const QString &collection)
+void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection)
{
- using CollectionEditorConstants::SourceFormat;
+ QJsonDocument document = readJsonFile(filePath);
- QFile sourceFile(source);
- QJsonArray collectionNodes;
- bool jsonFileIsOk = false;
- if (sourceFile.open(QFile::ReadOnly)) {
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe);
- if (jpe.error == QJsonParseError::NoError) {
- jsonFileIsOk = true;
- if (document.isObject()) {
- QJsonObject collectionMap = document.object();
- if (collectionMap.contains(collection)) {
- QJsonValue collectionVal = collectionMap.value(collection);
- if (collectionVal.isArray())
- collectionNodes = collectionVal.toArray();
- else
- collectionNodes.append(collectionVal);
- }
- }
- }
- }
-
- if (collectionNodes.isEmpty()) {
- endResetModel();
- return;
- };
-
- QList<QJsonObject> elements;
- for (const QJsonValue &value : std::as_const(collectionNodes)) {
- if (value.isObject()) {
- QJsonObject object = value.toObject();
- elements.append(object);
- }
- }
-
- SourceFormat sourceFormat = jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown;
beginResetModel();
- m_currentCollection.resetDetails(getJsonHeaders(collectionNodes), elements, sourceFormat);
- ensureSingleCell();
- endResetModel();
-}
-
-void CollectionDetailsModel::loadCsvCollection(const QString &source,
- [[maybe_unused]] const QString &collectionName)
-{
- using CollectionEditorConstants::SourceFormat;
-
- QFile sourceFile(source);
- QStringList headers;
- QList<QJsonObject> elements;
- bool csvFileIsOk = false;
-
- if (sourceFile.open(QFile::ReadOnly)) {
- QTextStream stream(&sourceFile);
-
- if (!stream.atEnd())
- headers = stream.readLine().split(',');
-
- if (!headers.isEmpty()) {
- while (!stream.atEnd()) {
- const QStringList recordDataList = stream.readLine().split(',');
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size())
- break;
- recordData.insert(headers.at(column), cellData);
- }
- if (recordData.count())
- elements.append(recordData);
- }
- csvFileIsOk = true;
- }
- }
-
- for (const QString &header : std::as_const(headers)) {
- for (QJsonObject &element: elements) {
- QVariant variantValue;
- if (element.contains(header)) {
- variantValue = variantFromString(element.value(header).toString());
- element[header] = variantValue.toJsonValue();
-
- if (variantValue.isValid())
- break;
- }
- }
- }
-
- SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown;
- beginResetModel();
- m_currentCollection.resetDetails(headers, elements, sourceFormat);
+ m_currentCollection.resetData(document, collection);
ensureSingleCell();
endResetModel();
}
void CollectionDetailsModel::ensureSingleCell()
{
- if (!m_currentCollection.isValid())
+ if (!m_currentCollection.hasValidReference())
return;
if (!columnCount())
@@ -748,6 +618,27 @@ QVariant CollectionDetailsModel::variantFromString(const QString &value)
return QVariant::fromValue(value);
}
+QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url)
+{
+ using Utils::FilePath;
+ using Utils::FileReader;
+ FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString());
+ FileReader file;
+
+ if (!file.fetch(path)) {
+ emit warning(tr("File reading problem"), file.errorString());
+ return {};
+ }
+
+ QJsonParseError jpe;
+ QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
+
+ if (jpe.error != QJsonParseError::NoError)
+ emit warning(tr("Json parse error"), jpe.errorString());
+
+ return document;
+}
+
void CollectionDetailsModel::setCollectionName(const QString &newCollectionName)
{
if (m_collectionName != newCollectionName) {
@@ -761,5 +652,4 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning
return DataTypeWarning::getDataTypeWarningString(warning);
}
-
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
index cef942b044..1fe152eaa2 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
@@ -68,11 +68,16 @@ public:
Q_INVOKABLE bool saveDataStoreCollections();
Q_INVOKABLE bool exportCollection(const QUrl &url);
+ const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const;
+ bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const;
+ QString getFirstColumnName(const CollectionReference &reference) const;
+
signals:
void collectionNameChanged(const QString &collectionName);
void selectedColumnChanged(int);
void selectedRowChanged(int);
void isEmptyChanged(bool);
+ void warning(const QString &title, const QString &body);
private slots:
void updateEmpty();
@@ -82,10 +87,10 @@ private:
void closeCollectionIfSaved(const CollectionReference &collection);
void closeCurrentCollectionIfSaved();
void setCollectionName(const QString &newCollectionName);
- void loadJsonCollection(const QString &source, const QString &collection);
- void loadCsvCollection(const QString &source, const QString &collectionName);
+ void loadJsonCollection(const QString &filePath, const QString &collection);
void ensureSingleCell();
QVariant variantFromString(const QString &value);
+ QJsonDocument readJsonFile(const QUrl &url);
QHash<CollectionReference, CollectionDetails> m_openedCollections;
CollectionDetails m_currentCollection;
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
index 4b779c52fa..9c77724932 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
@@ -24,19 +24,77 @@
#include <QJsonValue>
#include <QUrl>
+using DataType = QmlDesigner::CollectionDetails::DataType;
+
namespace {
using CollectionDataVariant = std::variant<QString, bool, double, int, QUrl, QColor>;
+class CollectionDataTypeHelper
+{
+private:
+ using DataType = QmlDesigner::CollectionDetails::DataType;
+ friend QString QmlDesigner::CollectionEditorUtils::dataTypeToString(DataType);
+ friend DataType QmlDesigner::CollectionEditorUtils::dataTypeFromString(const QString &);
+ friend QStringList QmlDesigner::CollectionEditorUtils::dataTypesStringList();
+
+ CollectionDataTypeHelper() = delete;
+
+ static QHash<DataType, QString> typeToStringHash()
+ {
+ return {
+ {DataType::Unknown, "Unknown"},
+ {DataType::String, "String"},
+ {DataType::Url, "Url"},
+ {DataType::Real, "Real"},
+ {DataType::Integer, "Integer"},
+ {DataType::Boolean, "Boolean"},
+ {DataType::Image, "Image"},
+ {DataType::Color, "Color"},
+ };
+ }
+
+ static QHash<QString, DataType> stringToTypeHash()
+ {
+ QHash<QString, DataType> stringTypeHash;
+ const QHash<DataType, QString> typeStringHash = typeToStringHash();
+ for (const auto &transferItem : typeStringHash.asKeyValueRange())
+ stringTypeHash.insert(transferItem.second, transferItem.first);
+
+ return stringTypeHash;
+ }
+
+ static QStringList orderedTypeNames()
+ {
+ const QList<DataType> orderedtypes{
+ DataType::String,
+ DataType::Integer,
+ DataType::Real,
+ DataType::Image,
+ DataType::Color,
+ DataType::Url,
+ DataType::Boolean,
+ DataType::Unknown,
+ };
+
+ QStringList orderedNames;
+ QHash<DataType, QString> typeStringHash = typeToStringHash();
+
+ for (const DataType &type : orderedtypes)
+ orderedNames.append(typeStringHash.take(type));
+
+ Q_ASSERT(typeStringHash.isEmpty());
+ return orderedNames;
+ }
+};
+
inline bool operator<(const QColor &a, const QColor &b)
{
return a.name(QColor::HexArgb) < b.name(QColor::HexArgb);
}
-inline CollectionDataVariant valueToVariant(const QVariant &value,
- QmlDesigner::CollectionDetails::DataType type)
+inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type)
{
- using DataType = QmlDesigner::CollectionDetails::DataType;
switch (type) {
case DataType::String:
return value.toString();
@@ -112,7 +170,7 @@ inline Utils::FilePath qmlDirFilePath()
namespace QmlDesigner::CollectionEditorUtils {
-bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type)
+bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type)
{
return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
}
@@ -210,16 +268,6 @@ bool isDataStoreNode(const ModelNode &dataStoreNode)
return modelPath.isSameFile(expectedFile);
}
-QJsonArray defaultCollectionArray()
-{
- QJsonObject initialObject;
- QJsonArray initialCollection;
-
- initialObject.insert("Column1", "");
- initialCollection.append(initialObject);
- return initialCollection;
-}
-
bool ensureDataStoreExists(bool &justCreated)
{
using Utils::FilePath;
@@ -306,178 +354,43 @@ bool ensureDataStoreExists(bool &justCreated)
return false;
}
-QJsonArray loadAsSingleJsonCollection(const QUrl &url)
+QJsonObject defaultCollection()
{
- QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
- QJsonArray collection;
- QByteArray jsonData;
- if (file.open(QFile::ReadOnly))
- jsonData = file.readAll();
-
- file.close();
- if (jsonData.isEmpty())
- return {};
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return {};
+ QJsonObject collectionObject;
- auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
- QJsonArray resultArray;
- for (const QJsonValue &collectionData : array) {
- if (collectionData.isObject()) {
- QJsonObject rowObject = collectionData.toObject();
- const QStringList rowKeys = rowObject.keys();
- for (const QString &key : rowKeys) {
- QJsonValue cellValue = rowObject.value(key);
- if (cellValue.isArray())
- rowObject.remove(key);
- }
- resultArray.push_back(rowObject);
- }
- }
- return resultArray;
- };
+ QJsonArray columns;
+ QJsonObject defaultColumn;
+ defaultColumn.insert("name", "Column 1");
+ defaultColumn.insert("type", "string");
+ columns.append(defaultColumn);
- if (document.isArray()) {
- collection = refineJsonArray(document.array());
- } else if (document.isObject()) {
- QJsonObject documentObject = document.object();
- const QStringList mainKeys = documentObject.keys();
-
- bool arrayFound = false;
- for (const QString &key : mainKeys) {
- const QJsonValue &value = documentObject.value(key);
- if (value.isArray()) {
- arrayFound = true;
- collection = refineJsonArray(value.toArray());
- break;
- }
- }
+ QJsonArray collectionData;
+ QJsonArray cellData;
+ cellData.append(QString{});
+ collectionData.append(cellData);
- if (!arrayFound) {
- QJsonObject singleObject;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
+ collectionObject.insert("columns", columns);
+ collectionObject.insert("data", collectionData);
- if (!value.isObject())
- singleObject.insert(key, value);
- }
- collection.push_back(singleObject);
- }
- }
- return collection;
+ return collectionObject;
}
-QJsonArray loadAsCsvCollection(const QUrl &url)
+QString dataTypeToString(CollectionDetails::DataType dataType)
{
- QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString());
- QStringList headers;
- QJsonArray elements;
-
- if (sourceFile.open(QFile::ReadOnly)) {
- QTextStream stream(&sourceFile);
-
- if (!stream.atEnd())
- headers = stream.readLine().split(',');
-
- for (QString &header : headers)
- header = header.trimmed();
-
- if (!headers.isEmpty()) {
- while (!stream.atEnd()) {
- const QStringList recordDataList = stream.readLine().split(',');
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size())
- break;
- recordData.insert(headers.at(column), cellData);
- }
- elements.append(recordData);
- }
- }
- }
-
- return elements;
+ static const QHash<DataType, QString> typeStringHash = CollectionDataTypeHelper::typeToStringHash();
+ return typeStringHash.value(dataType);
}
-QString getFirstColumnName(const QString &collectionName)
+CollectionDetails::DataType dataTypeFromString(const QString &dataType)
{
- Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath();
-
- if (!dataStorePath.exists())
- return {};
-
- Utils::FileReader dataStoreFile;
- if (!dataStoreFile.fetch(dataStorePath))
- return {};
-
- QJsonParseError jsonError;
- QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError);
- if (jsonError.error == QJsonParseError::NoError) {
- QJsonObject rootObject = dataStoreDocument.object();
- if (rootObject.contains(collectionName)) {
- QJsonArray collectionArray = rootObject.value(collectionName).toArray();
- for (const QJsonValue &elementValue : std::as_const(collectionArray)) {
- const QJsonObject elementObject = elementValue.toObject();
- QJsonObject::ConstIterator element = elementObject.constBegin();
- if (element != elementObject.constEnd())
- return element.key();
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__
- << QString("Collection \"%1\" not found.").arg(collectionName);
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file."
- << jsonError.errorString();
- }
-
- return {};
+ static const QHash<QString, DataType> stringTypeHash = CollectionDataTypeHelper::stringToTypeHash();
+ return stringTypeHash.value(dataType, DataType::Unknown);
}
-bool collectionHasColumn(const QString &collectionName, const QString &columnName)
+QStringList dataTypesStringList()
{
- Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath();
-
- if (!dataStorePath.exists())
- return false;
-
- Utils::FileReader dataStoreFile;
- if (!dataStoreFile.fetch(dataStorePath))
- return false;
-
- QJsonParseError jsonError;
- QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError);
- if (jsonError.error == QJsonParseError::NoError) {
- QJsonObject rootObject = dataStoreDocument.object();
- if (rootObject.contains(collectionName)) {
- QJsonArray collectionArray = rootObject.value(collectionName).toArray();
- for (const QJsonValue &elementValue : std::as_const(collectionArray)) {
- const QJsonObject elementObject = elementValue.toObject();
- QJsonObject::ConstIterator element = elementObject.constBegin();
- const QJsonObject::ConstIterator stopItem = elementObject.constEnd();
-
- while (element != stopItem) {
- const QString keyName = element.key();
- ++element;
-
- if (columnName == keyName)
- return true;
- }
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__
- << QString("Collection \"%1\" not found.").arg(collectionName);
- }
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file."
- << jsonError.errorString();
- }
-
- return false;
+ static const QStringList typesList = CollectionDataTypeHelper::orderedTypeNames();
+ return typesList;
}
} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
index 46429f04b6..5f59329fc6 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
@@ -8,6 +8,7 @@
QT_BEGIN_NAMESPACE
class QJsonArray;
+class QJsonObject;
QT_END_NAMESPACE
namespace Utils {
@@ -36,14 +37,12 @@ bool canAcceptCollectionAsModel(const ModelNode &node);
bool hasTextRoleProperty(const ModelNode &node);
-QJsonArray defaultCollectionArray();
+QJsonObject defaultCollection();
-QJsonArray loadAsSingleJsonCollection(const QUrl &url);
+QString dataTypeToString(CollectionDetails::DataType dataType);
-QJsonArray loadAsCsvCollection(const QUrl &url);
+CollectionDetails::DataType dataTypeFromString(const QString &dataType);
-QString getFirstColumnName(const QString &collectionName);
-
-bool collectionHasColumn(const QString &collectionName, const QString &columnName);
+QStringList dataTypesStringList();
} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp
index 1d27f20548..9378d6871c 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp
@@ -270,7 +270,7 @@ bool CollectionSourceModel::collectionExists(const ModelNode &node, const QStrin
bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
const QString &collectionName,
- const QJsonArray &newCollectionData,
+ const QJsonObject &newCollection,
QString *errorString)
{
auto returnError = [errorString](const QString &msg) -> bool {
@@ -308,7 +308,7 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node,
if (document.isObject()) {
QJsonObject sourceObject = document.object();
- sourceObject.insert(collectionName, newCollectionData);
+ sourceObject.insert(collectionName, newCollection);
document.setObject(sourceObject);
if (!jsonFile.resize(0))
return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h
index 5ab77f2a98..ac01c0de22 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h
@@ -54,7 +54,7 @@ public:
bool collectionExists(const ModelNode &node, const QString &collectionName) const;
bool addCollectionToSource(const ModelNode &node,
const QString &collectionName,
- const QJsonArray &newCollectionData,
+ const QJsonObject &newCollection,
QString *errorString = nullptr);
ModelNode sourceNodeAt(int idx);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
index 1b1d1a7d9f..f2075b4abb 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
@@ -226,7 +226,18 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
{
QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
- m_dataStore->assignCollectionToNode(this, singleSelectedModelNode(), collectionName);
+ m_dataStore->assignCollectionToNode(
+ this,
+ singleSelectedModelNode(),
+ collectionName,
+ [&](const QString &collectionName, const QString &columnName) -> bool {
+ const CollectionReference reference{dataStoreNode(), collectionName};
+ return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName);
+ },
+ [&](const QString &collectionName) -> QString {
+ const CollectionReference reference{dataStoreNode(), collectionName};
+ return m_widget->collectionDetailsModel()->getFirstColumnName(reference);
+ });
}
void CollectionView::registerDeclarativeType()
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
index a123cf3361..efffa2c2e9 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
@@ -3,6 +3,7 @@
#include "collectionwidget.h"
+#include "collectiondetails.h"
#include "collectiondetailsmodel.h"
#include "collectiondetailssortfiltermodel.h"
#include "collectioneditorutils.h"
@@ -215,7 +216,7 @@ bool CollectionWidget::addCollection(const QString &collectionName,
if (collectionType == "json") {
QJsonObject jsonObject;
- jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollectionArray());
+ jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollection());
QFile sourceFile(sourcePath);
if (!sourceFile.open(QFile::WriteOnly)) {
@@ -257,8 +258,10 @@ bool CollectionWidget::addCollection(const QString &collectionName,
}
} else if (collectionType == "json") {
QString errorMsg;
- bool added = m_sourceModel->addCollectionToSource(
- node, collectionName, CollectionEditorUtils::defaultCollectionArray(), &errorMsg);
+ bool added = m_sourceModel->addCollectionToSource(node,
+ collectionName,
+ CollectionEditorUtils::defaultCollection(),
+ &errorMsg);
if (!added)
warn(tr("Can not add a model to the JSON file"), errorMsg);
return added;
@@ -267,48 +270,69 @@ bool CollectionWidget::addCollection(const QString &collectionName,
return false;
}
-bool CollectionWidget::importToJson(const QVariant &sourceNode,
- const QString &collectionName,
- const QUrl &url)
+bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url)
{
- using CollectionEditorConstants::SourceFormat;
using Utils::FilePath;
- const ModelNode node = sourceNode.value<ModelNode>();
- const SourceFormat nodeFormat = CollectionEditorUtils::getSourceCollectionFormat(node);
- QTC_ASSERT(node.isValid() && nodeFormat == SourceFormat::Json, return false);
+ ensureDataStoreExists();
+
+ const ModelNode node = dataStoreNode();
+ if (!node.isValid()) {
+ warn(tr("Can not import to the main model"), tr("The data store is not available."));
+ return false;
+ }
FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
: url.toString());
bool added = false;
QString errorMsg;
- QJsonArray loadedCollection;
+ CollectionDetails loadedCollection;
+ QByteArray fileContent;
- if (fileInfo.suffix() == "json")
- loadedCollection = CollectionEditorUtils::loadAsSingleJsonCollection(url);
- else if (fileInfo.suffix() == "csv")
- loadedCollection = CollectionEditorUtils::loadAsCsvCollection(url);
+ auto loadUrlContent = [&]() -> bool {
+ QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
+
+ if (file.open(QFile::ReadOnly)) {
+ fileContent = file.readAll();
+ file.close();
+ return true;
+ }
+
+ warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(file.fileName()));
+ return false;
+ };
+
+ if (fileInfo.suffix() == "json") {
+ if (!loadUrlContent())
+ return false;
- if (!loadedCollection.isEmpty()) {
+ QJsonParseError parseError;
+ QJsonDocument document = QJsonDocument::fromJson(fileContent, &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ warn(tr("Json file Import error"),
+ tr("Cannot parse json content\n%1").arg(parseError.errorString()));
+ }
+
+ loadedCollection = CollectionDetails::fromImportedJson(document);
+ } else if (fileInfo.suffix() == "csv") {
+ if (!loadUrlContent())
+ return false;
+ loadedCollection = CollectionDetails::fromImportedCsv(fileContent);
+ }
+
+ if (loadedCollection.columns()) {
const QString newCollectionName = generateUniqueCollectionName(node, collectionName);
- added = m_sourceModel->addCollectionToSource(node, newCollectionName, loadedCollection, &errorMsg);
+ added = m_sourceModel->addCollectionToSource(node,
+ newCollectionName,
+ loadedCollection.toLocalJson(),
+ &errorMsg);
} else {
errorMsg = tr("The imported model is empty or is not supported.");
}
if (!added)
warn(tr("Can not add a model to the JSON file"), errorMsg);
- return added;
-}
-bool CollectionWidget::importCollectionToDataStore(const QString &collectionName, const QUrl &url)
-{
- using Utils::FilePath;
- const ModelNode node = dataStoreNode();
- if (node.isValid())
- return importToJson(QVariant::fromValue(node), collectionName, url);
-
- warn(tr("Can not import to the main model"), tr("The data store is not available."));
- return false;
+ return added;
}
bool CollectionWidget::addCollectionToDataStore(const QString &collectionName)
@@ -324,7 +348,7 @@ bool CollectionWidget::addCollectionToDataStore(const QString &collectionName)
bool added = m_sourceModel->addCollectionToSource(node,
generateUniqueCollectionName(node,
collectionName),
- CollectionEditorUtils::defaultCollectionArray(),
+ CollectionEditorUtils::defaultCollection(),
&errorMsg);
if (!added)
warn(tr("Failed to add a model to the default model group"), errorMsg);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
index 2be98df190..5a9c45d591 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
@@ -44,18 +44,10 @@ public:
const QUrl &sourceUrl,
const QVariant &sourceNode);
- Q_INVOKABLE bool importToJson(const QVariant &sourceNode,
- const QString &collectionName,
- const QUrl &url);
-
- Q_INVOKABLE bool importCollectionToDataStore(const QString &collectionName, const QUrl &url);
-
+ Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url);
Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName);
-
Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName);
-
Q_INVOKABLE void ensureDataStoreExists();
-
Q_INVOKABLE ModelNode dataStoreNode() const;
void warn(const QString &title, const QString &body);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
index a1f82bbc65..b85c651785 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
+++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
@@ -159,7 +159,9 @@ void DataStoreModelNode::reloadModel()
reset();
}
- QTC_ASSERT(m_model.get(), return);
+ if (!m_model.get())
+ return;
+
m_model->setFileUrl(dataStoreQmlUrl);
m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
@@ -182,7 +184,8 @@ Model *DataStoreModelNode::model() const
ModelNode DataStoreModelNode::modelNode() const
{
- QTC_ASSERT(m_model.get(), return {});
+ if (!m_model.get())
+ return {};
return m_model->rootModelNode();
}
@@ -427,7 +430,9 @@ void DataStoreModelNode::removeCollection(const QString &collectionName)
void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
- const QString &collectionName)
+ const QString &collectionName,
+ CollectionColumnFinder collectionHasColumn,
+ FirstColumnProvider firstColumnProvider)
{
QTC_ASSERT(targetNode.isValid(), return);
@@ -461,14 +466,14 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) {
if (currentTextRoleValue.type() == QVariant::String) {
const QString currentTextRole = currentTextRoleValue.toString();
- if (CollectionEditorUtils::collectionHasColumn(collectionName, currentTextRole))
+ if (collectionHasColumn(collectionName, currentTextRole))
return;
} else {
return;
}
}
- QString textRoleValue = CollectionEditorUtils::getFirstColumnName(collectionName);
+ QString textRoleValue = firstColumnProvider(collectionName);
textRoleProperty.setValue(textRoleValue);
}
});
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
index 1c855bca7a..d23908bc0c 100644
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
+++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
@@ -18,6 +18,10 @@ class Model;
class DataStoreModelNode
{
public:
+ using CollectionColumnFinder = std::function<bool(const QString &collectionName,
+ const QString &columnName)>;
+ using FirstColumnProvider = std::function<QString(const QString &collectionName)>;
+
DataStoreModelNode();
void reloadModel();
@@ -32,7 +36,9 @@ public:
void assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
- const QString &collectionName);
+ const QString &collectionName,
+ CollectionColumnFinder collectionHasColumn,
+ FirstColumnProvider firstColumnProvider);
private:
QString getModelQmlText();