@@ -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" |
@@ -20,6 +20,7 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers) | |||
|
20 | 20 | |
|
21 | 21 | core_sources = [ |
|
22 | 22 | 'src/Common/DateUtils.cpp', |
|
23 | 'src/Common/StringUtils.cpp', | |
|
23 | 24 | 'src/Data/ScalarSeries.cpp', |
|
24 | 25 | 'src/Data/DataSeriesIterator.cpp', |
|
25 | 26 | 'src/Data/ArrayDataIterator.cpp', |
@@ -2,6 +2,7 | |||
|
2 | 2 | #include <Variable/VariableModel.h> |
|
3 | 3 | |
|
4 | 4 | #include <Common/DateUtils.h> |
|
5 | #include <Common/StringUtils.h> | |
|
5 | 6 | |
|
6 | 7 | #include <Data/IDataSeries.h> |
|
7 | 8 | |
@@ -45,6 +46,17 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{ | |||
|
45 | 46 | /// Format for datetimes |
|
46 | 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 | 61 | } // namespace |
|
50 | 62 | |
@@ -67,6 +79,9 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept | |||
|
67 | 79 | auto insertIndex = rowCount(); |
|
68 | 80 | beginInsertRows({}, insertIndex, insertIndex); |
|
69 | 81 | |
|
82 | // Generates unique name for the variable | |
|
83 | variable->setName(uniqueName(variable->name(), impl->m_Variables)); | |
|
84 | ||
|
70 | 85 | impl->m_Variables.push_back(variable); |
|
71 | 86 | connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated); |
|
72 | 87 |
@@ -1,6 +1,7 | |||
|
1 | 1 | |
|
2 | 2 | |
|
3 | 3 | tests = [ |
|
4 | [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'], | |
|
4 | 5 | [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'], |
|
5 | 6 | [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'], |
|
6 | 7 | [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'], |
General Comments 0
You need to be logged in to leave comments.
Login now