##// END OF EJS Templates
Merge branch 'feature/CloneVariable' into develop
Alexandre Leroux -
r714:ceac74ef218a merge
parent child
Show More
@@ -0,0 +1,35
1 #ifndef SCIQLOP_STRINGUTILS_H
2 #define SCIQLOP_STRINGUTILS_H
3
4 #include "CoreGlobal.h"
5
6 #include <vector>
7
8 class QString;
9
10 /**
11 * Utility class with methods for strings
12 */
13 struct SCIQLOP_CORE_EXPORT StringUtils {
14 /**
15 * Generates a unique name from a default name and a set of forbidden names.
16 *
17 * Generating the unique name is done by adding an index to the default name and stopping at the
18 * first index for which the generated name is not in the forbidden names.
19 *
20 * Examples (defaultName, forbiddenNames -> result):
21 * - "FGM", {"FGM"} -> "FGM1"
22 * - "FGM", {"ABC"} -> "FGM"
23 * - "FGM", {"FGM", "FGM1"} -> "FGM2"
24 * - "FGM", {"FGM", "FGM2"} -> "FGM1"
25 * - "", {"ABC"} -> "1"
26 *
27 * @param defaultName the default name
28 * @param forbiddenNames the set of forbidden names
29 * @return the unique name generated
30 */
31 static QString uniqueName(const QString &defaultName,
32 const std::vector<QString> &forbiddenNames) noexcept;
33 };
34
35 #endif // SCIQLOP_STRINGUTILS_H
@@ -0,0 +1,30
1 #include "Common/StringUtils.h"
2
3 #include <QRegExp>
4 #include <QString>
5
6 #include <set>
7
8 QString StringUtils::uniqueName(const QString &defaultName,
9 const std::vector<QString> &forbiddenNames) noexcept
10 {
11 // Gets the base of the unique name to generate, by removing trailing number (for example, base
12 // name of "FGM12" is "FGM")
13 auto baseName = defaultName;
14 baseName.remove(QRegExp{QStringLiteral("\\d*$")});
15
16 // Finds the unique name by adding an index to the base name and stops when the generated name
17 // isn't forbidden
18 QString newName{};
19 auto forbidden = true;
20 for (auto i = 0; forbidden; ++i) {
21 newName = (i == 0) ? baseName : baseName + QString::number(i);
22 forbidden = newName.isEmpty()
23 || std::any_of(forbiddenNames.cbegin(), forbiddenNames.cend(),
24 [&newName](const auto &name) {
25 return name.compare(newName, Qt::CaseInsensitive) == 0;
26 });
27 }
28
29 return newName;
30 }
@@ -0,0 +1,50
1 #include <Common/StringUtils.h>
2
3 #include <QObject>
4 #include <QtTest>
5
6 class TestStringUtils : public QObject {
7 Q_OBJECT
8
9 private slots:
10 void testUniqueName_data();
11 void testUniqueName();
12 };
13
14 void TestStringUtils::testUniqueName_data()
15 {
16 // ////////////// //
17 // Test structure //
18 // ////////////// //
19
20 QTest::addColumn<QString>("defaultName");
21 QTest::addColumn<std::vector<QString> >("forbiddenNames");
22 QTest::addColumn<QString>("expectedName");
23
24 // ////////// //
25 // Test cases //
26 // ////////// //
27
28 QTest::newRow("uniqueName") << "FGM" << std::vector<QString>{"FGM2"} << "FGM";
29 QTest::newRow("uniqueName2") << "FGM2" << std::vector<QString>{"FGM", "FGM1", "FGM2"} << "FGM3";
30 QTest::newRow("uniqueName3") << "FGM1" << std::vector<QString>{"FGM1"} << "FGM";
31 QTest::newRow("uniqueName4") << "FGM" << std::vector<QString>{"FGM"} << "FGM1";
32 QTest::newRow("uniqueName5") << "FGM" << std::vector<QString>{"FGM", "FGM1", "FGM3"} << "FGM2";
33 QTest::newRow("uniqueName6") << "FGM" << std::vector<QString>{"A", "B", "C"} << "FGM";
34 QTest::newRow("uniqueName7") << "FGM" << std::vector<QString>{"fGm", "FGm1", "Fgm2"} << "FGM3";
35 QTest::newRow("uniqueName8") << "" << std::vector<QString>{"A", "B", "C"} << "1";
36 QTest::newRow("uniqueName9") << "24" << std::vector<QString>{"A", "B", "C"} << "1";
37 }
38
39 void TestStringUtils::testUniqueName()
40 {
41 QFETCH(QString, defaultName);
42 QFETCH(std::vector<QString>, forbiddenNames);
43 QFETCH(QString, expectedName);
44
45 auto result = StringUtils::uniqueName(defaultName, forbiddenNames);
46 QCOMPARE(result, expectedName);
47 }
48
49 QTEST_MAIN(TestStringUtils)
50 #include "TestStringUtils.moc"
@@ -32,6 +32,7 class SCIQLOP_CORE_EXPORT IDataProvider : public QObject {
32 Q_OBJECT
32 Q_OBJECT
33 public:
33 public:
34 virtual ~IDataProvider() noexcept = default;
34 virtual ~IDataProvider() noexcept = default;
35 virtual std::shared_ptr<IDataProvider> clone() const = 0;
35
36
36 /**
37 /**
37 * @brief requestDataLoading provide datas for the data identified by acqIdentifier and
38 * @brief requestDataLoading provide datas for the data identified by acqIdentifier and
@@ -28,6 +28,11 public:
28 explicit Variable(const QString &name, const SqpRange &dateTime,
28 explicit Variable(const QString &name, const SqpRange &dateTime,
29 const QVariantHash &metadata = {});
29 const QVariantHash &metadata = {});
30
30
31 /// Copy ctor
32 explicit Variable(const Variable &other);
33
34 std::shared_ptr<Variable> clone() const;
35
31 QString name() const noexcept;
36 QString name() const noexcept;
32 void setName(const QString &name) noexcept;
37 void setName(const QString &name) noexcept;
33 SqpRange range() const noexcept;
38 SqpRange range() const noexcept;
@@ -42,6 +42,13 public:
42 void setTimeController(TimeController *timeController) noexcept;
42 void setTimeController(TimeController *timeController) noexcept;
43
43
44 /**
44 /**
45 * Clones the variable passed in parameter and adds the duplicate to the controller
46 * @param variable the variable to duplicate
47 * @return the duplicate created, nullptr if the variable couldn't be created
48 */
49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50
51 /**
45 * Deletes from the controller the variable passed in parameter.
52 * Deletes from the controller the variable passed in parameter.
46 *
53 *
47 * Delete a variable includes:
54 * Delete a variable includes:
@@ -28,6 +28,21 public:
28 explicit VariableModel(QObject *parent = nullptr);
28 explicit VariableModel(QObject *parent = nullptr);
29
29
30 /**
30 /**
31 * Adds an existing variable in the model.
32 * @param variable the variable to add.
33 * @remarks the variable's name is modified to avoid name duplicates
34 * @remarks this method does nothing if the variable already exists in the model
35 */
36 void addVariable(std::shared_ptr<Variable> variable) noexcept;
37
38 /**
39 * Checks that a variable is contained in the model
40 * @param variable the variable to check
41 * @return true if the variable is in the model, false otherwise
42 */
43 bool containsVariable(std::shared_ptr<Variable> variable) const noexcept;
44
45 /**
31 * Creates a new variable in the model
46 * Creates a new variable in the model
32 * @param name the name of the new variable
47 * @param name the name of the new variable
33 * @param dateTime the dateTime of the new variable
48 * @param dateTime the dateTime of the new variable
@@ -20,6 +20,7 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
20
20
21 core_sources = [
21 core_sources = [
22 'src/Common/DateUtils.cpp',
22 'src/Common/DateUtils.cpp',
23 'src/Common/StringUtils.cpp',
23 'src/Data/ScalarSeries.cpp',
24 'src/Data/ScalarSeries.cpp',
24 'src/Data/DataSeriesIterator.cpp',
25 'src/Data/DataSeriesIterator.cpp',
25 'src/Data/ArrayDataIterator.cpp',
26 'src/Data/ArrayDataIterator.cpp',
@@ -20,6 +20,15 struct Variable::VariablePrivate {
20 {
20 {
21 }
21 }
22
22
23 VariablePrivate(const VariablePrivate &other)
24 : m_Name{other.m_Name},
25 m_Range{other.m_Range},
26 m_Metadata{other.m_Metadata},
27 m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr},
28 m_RealRange{other.m_RealRange}
29 {
30 }
31
23 void lockRead() { m_Lock.lockForRead(); }
32 void lockRead() { m_Lock.lockForRead(); }
24 void lockWrite() { m_Lock.lockForWrite(); }
33 void lockWrite() { m_Lock.lockForWrite(); }
25 void unlock() { m_Lock.unlock(); }
34 void unlock() { m_Lock.unlock(); }
@@ -67,6 +76,16 Variable::Variable(const QString &name, const SqpRange &dateTime, const QVariant
67 {
76 {
68 }
77 }
69
78
79 Variable::Variable(const Variable &other)
80 : impl{spimpl::make_unique_impl<VariablePrivate>(*other.impl)}
81 {
82 }
83
84 std::shared_ptr<Variable> Variable::clone() const
85 {
86 return std::make_shared<Variable>(*this);
87 }
88
70 QString Variable::name() const noexcept
89 QString Variable::name() const noexcept
71 {
90 {
72 impl->lockRead();
91 impl->lockRead();
@@ -188,6 +188,38 void VariableController::setTimeController(TimeController *timeController) noexc
188 impl->m_TimeController = timeController;
188 impl->m_TimeController = timeController;
189 }
189 }
190
190
191 std::shared_ptr<Variable>
192 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
193 {
194 if (impl->m_VariableModel->containsVariable(variable)) {
195 // Clones variable
196 auto duplicate = variable->clone();
197
198 // Adds clone to model
199 impl->m_VariableModel->addVariable(duplicate);
200
201 // Generates clone identifier
202 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
203
204 // Registers provider
205 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
206 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
207
208 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
209 if (duplicateProvider) {
210 impl->registerProvider(duplicateProvider);
211 }
212
213 return duplicate;
214 }
215 else {
216 qCCritical(LOG_VariableController())
217 << tr("Can't create duplicate of variable %1: variable not registered in the model")
218 .arg(variable->name());
219 return nullptr;
220 }
221 }
222
191 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
223 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
192 {
224 {
193 if (!variable) {
225 if (!variable) {
@@ -2,6 +2,7
2 #include <Variable/VariableModel.h>
2 #include <Variable/VariableModel.h>
3
3
4 #include <Common/DateUtils.h>
4 #include <Common/DateUtils.h>
5 #include <Common/StringUtils.h>
5
6
6 #include <Data/IDataSeries.h>
7 #include <Data/IDataSeries.h>
7
8
@@ -45,6 +46,17 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
45 /// Format for datetimes
46 /// Format for datetimes
46 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
47 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
47
48
49 QString uniqueName(const QString &defaultName,
50 const std::vector<std::shared_ptr<Variable> > &variables)
51 {
52 auto forbiddenNames = std::vector<QString>(variables.size());
53 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
54 [](const auto &variable) { return variable->name(); });
55 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
56 Q_ASSERT(!uniqueName.isEmpty());
57
58 return uniqueName;
59 }
48
60
49 } // namespace
61 } // namespace
50
62
@@ -62,19 +74,32 VariableModel::VariableModel(QObject *parent)
62 {
74 {
63 }
75 }
64
76
65 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
77 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
66 const SqpRange &dateTime,
67 const QVariantHash &metadata) noexcept
68 {
78 {
69 auto insertIndex = rowCount();
79 auto insertIndex = rowCount();
70 beginInsertRows({}, insertIndex, insertIndex);
80 beginInsertRows({}, insertIndex, insertIndex);
71
81
72 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
82 // Generates unique name for the variable
83 variable->setName(uniqueName(variable->name(), impl->m_Variables));
73
84
74 impl->m_Variables.push_back(variable);
85 impl->m_Variables.push_back(variable);
75 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
86 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
76
87
77 endInsertRows();
88 endInsertRows();
89 }
90
91 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
92 {
93 auto end = impl->m_Variables.cend();
94 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
95 }
96
97 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
98 const SqpRange &dateTime,
99 const QVariantHash &metadata) noexcept
100 {
101 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
102 addVariable(variable);
78
103
79 return variable;
104 return variable;
80 }
105 }
@@ -12,6 +12,8 namespace {
12
12
13 /// Provider used for the tests
13 /// Provider used for the tests
14 class TestProvider : public IDataProvider {
14 class TestProvider : public IDataProvider {
15 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
16
15 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
17 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
16 {
18 {
17 // Does nothing
19 // Does nothing
@@ -1,6 +1,7
1
1
2
2
3 tests = [
3 tests = [
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
4 [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'],
5 [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'],
5 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
6 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
6 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
@@ -25,7 +25,7 RenameVariableDialog::~RenameVariableDialog() noexcept
25
25
26 QString RenameVariableDialog::name() const noexcept
26 QString RenameVariableDialog::name() const noexcept
27 {
27 {
28 return ui->nameLineEdit->text();
28 return ui->nameLineEdit->text().trimmed();
29 }
29 }
30
30
31 void RenameVariableDialog::accept()
31 void RenameVariableDialog::accept()
@@ -38,7 +38,7 void RenameVariableDialog::accept()
38 };
38 };
39
39
40 // Empty name
40 // Empty name
41 auto name = ui->nameLineEdit->text();
41 auto name = this->name();
42 if (name.isEmpty()) {
42 if (name.isEmpty()) {
43 invalidateInput(tr("A variable name must be specified"));
43 invalidateInput(tr("A variable name must be specified"));
44 return;
44 return;
@@ -176,10 +176,16 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
176 if (!selectedVariables.isEmpty()) {
176 if (!selectedVariables.isEmpty()) {
177 tableMenu.addSeparator();
177 tableMenu.addSeparator();
178
178
179 // 'Rename' action (only if one variable selected)
179 // 'Rename' and 'Duplicate' actions (only if one variable selected)
180 if (selectedVariables.size() == 1) {
180 if (selectedVariables.size() == 1) {
181 auto selectedVariable = selectedVariables.front();
181 auto selectedVariable = selectedVariables.front();
182
182
183 auto duplicateFun = [&selectedVariable]() {
184 sqpApp->variableController().cloneVariable(selectedVariable);
185 };
186
187 tableMenu.addAction(tr("Duplicate"), duplicateFun);
188
183 auto renameFun = [&selectedVariable, &model, this]() {
189 auto renameFun = [&selectedVariable, &model, this]() {
184 // Generates forbidden names (names associated to existing variables)
190 // Generates forbidden names (names associated to existing variables)
185 auto allVariables = model->variables();
191 auto allVariables = model->variables();
@@ -18,6 +18,7 class QNetworkReply;
18 class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider {
18 class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider {
19 public:
19 public:
20 explicit AmdaProvider();
20 explicit AmdaProvider();
21 std::shared_ptr<IDataProvider> clone() const override;
21
22
22 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
23 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
23
24
@@ -68,6 +68,12 AmdaProvider::AmdaProvider()
68 }
68 }
69 }
69 }
70
70
71 std::shared_ptr<IDataProvider> AmdaProvider::clone() const
72 {
73 // No copy is made in the clone
74 return std::make_shared<AmdaProvider>();
75 }
76
71 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
77 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
72 {
78 {
73 // NOTE: Try to use multithread if possible
79 // NOTE: Try to use multithread if possible
@@ -16,6 +16,8 Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
16 */
16 */
17 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
17 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
18 public:
18 public:
19 std::shared_ptr<IDataProvider> clone() const override;
20
19 /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe.
21 /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe.
20 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
22 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
21
23
@@ -11,6 +11,12
11
11
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13
13
14 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
15 {
16 // No copy is made in clone
17 return std::make_shared<CosinusProvider>();
18 }
19
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
20 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
15 const SqpRange &dataRangeRequested)
21 const SqpRange &dataRangeRequested)
16 {
22 {
General Comments 0
You need to be logged in to leave comments. Login now