From 8d80a1dd40592b7f67fc595bd03f8cad6d688b76 2017-09-07 09:47:07 From: Alexandre Leroux Date: 2017-09-07 09:47:07 Subject: [PATCH] Generates unique name for variable duplicate --- diff --git a/core/include/Common/StringUtils.h b/core/include/Common/StringUtils.h new file mode 100644 index 0000000..3dcdee2 --- /dev/null +++ b/core/include/Common/StringUtils.h @@ -0,0 +1,35 @@ +#ifndef SCIQLOP_STRINGUTILS_H +#define SCIQLOP_STRINGUTILS_H + +#include "CoreGlobal.h" + +#include + +class QString; + +/** + * Utility class with methods for strings + */ +struct SCIQLOP_CORE_EXPORT StringUtils { + /** + * Generates a unique name from a default name and a set of forbidden names. + * + * Generating the unique name is done by adding an index to the default name and stopping at the + * first index for which the generated name is not in the forbidden names. + * + * Examples (defaultName, forbiddenNames -> result): + * - "FGM", {"FGM"} -> "FGM1" + * - "FGM", {"ABC"} -> "FGM" + * - "FGM", {"FGM", "FGM1"} -> "FGM2" + * - "FGM", {"FGM", "FGM2"} -> "FGM1" + * - "", {"ABC"} -> "1" + * + * @param defaultName the default name + * @param forbiddenNames the set of forbidden names + * @return the unique name generated + */ + static QString uniqueName(const QString &defaultName, + const std::vector &forbiddenNames) noexcept; +}; + +#endif // SCIQLOP_STRINGUTILS_H diff --git a/core/meson.build b/core/meson.build index bb77c2e..195cd0b 100644 --- a/core/meson.build +++ b/core/meson.build @@ -20,6 +20,7 @@ core_moc_files = qt5.preprocess(moc_headers : core_moc_headers) core_sources = [ 'src/Common/DateUtils.cpp', + 'src/Common/StringUtils.cpp', 'src/Data/ScalarSeries.cpp', 'src/Data/DataSeriesIterator.cpp', 'src/Data/ArrayDataIterator.cpp', diff --git a/core/src/Common/StringUtils.cpp b/core/src/Common/StringUtils.cpp new file mode 100644 index 0000000..784e313 --- /dev/null +++ b/core/src/Common/StringUtils.cpp @@ -0,0 +1,30 @@ +#include "Common/StringUtils.h" + +#include +#include + +#include + +QString StringUtils::uniqueName(const QString &defaultName, + const std::vector &forbiddenNames) noexcept +{ + // Gets the base of the unique name to generate, by removing trailing number (for example, base + // name of "FGM12" is "FGM") + auto baseName = defaultName; + baseName.remove(QRegExp{QStringLiteral("\\d*$")}); + + // Finds the unique name by adding an index to the base name and stops when the generated name + // isn't forbidden + QString newName{}; + auto forbidden = true; + for (auto i = 0; forbidden; ++i) { + newName = (i == 0) ? baseName : baseName + QString::number(i); + forbidden = newName.isEmpty() + || std::any_of(forbiddenNames.cbegin(), forbiddenNames.cend(), + [&newName](const auto &name) { + return name.compare(newName, Qt::CaseInsensitive) == 0; + }); + } + + return newName; +} diff --git a/core/src/Variable/VariableModel.cpp b/core/src/Variable/VariableModel.cpp index f0212ab..1ba3792 100644 --- a/core/src/Variable/VariableModel.cpp +++ b/core/src/Variable/VariableModel.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -45,6 +46,17 @@ const auto COLUMN_PROPERTIES = QHash{ /// Format for datetimes const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz"); +QString uniqueName(const QString &defaultName, + const std::vector > &variables) +{ + auto forbiddenNames = std::vector(variables.size()); + std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(), + [](const auto &variable) { return variable->name(); }); + auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames); + Q_ASSERT(!uniqueName.isEmpty()); + + return uniqueName; +} } // namespace @@ -67,6 +79,9 @@ void VariableModel::addVariable(std::shared_ptr variable) noexcept auto insertIndex = rowCount(); beginInsertRows({}, insertIndex, insertIndex); + // Generates unique name for the variable + variable->setName(uniqueName(variable->name(), impl->m_Variables)); + impl->m_Variables.push_back(variable); connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated); diff --git a/core/tests/Common/TestStringUtils.cpp b/core/tests/Common/TestStringUtils.cpp new file mode 100644 index 0000000..c6f66f1 --- /dev/null +++ b/core/tests/Common/TestStringUtils.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include + +class TestStringUtils : public QObject { + Q_OBJECT + +private slots: + void testUniqueName_data(); + void testUniqueName(); +}; + +void TestStringUtils::testUniqueName_data() +{ + // ////////////// // + // Test structure // + // ////////////// // + + QTest::addColumn("defaultName"); + QTest::addColumn >("forbiddenNames"); + QTest::addColumn("expectedName"); + + // ////////// // + // Test cases // + // ////////// // + + QTest::newRow("uniqueName") << "FGM" << std::vector{"FGM2"} << "FGM"; + QTest::newRow("uniqueName2") << "FGM2" << std::vector{"FGM", "FGM1", "FGM2"} << "FGM3"; + QTest::newRow("uniqueName3") << "FGM1" << std::vector{"FGM1"} << "FGM"; + QTest::newRow("uniqueName4") << "FGM" << std::vector{"FGM"} << "FGM1"; + QTest::newRow("uniqueName5") << "FGM" << std::vector{"FGM", "FGM1", "FGM3"} << "FGM2"; + QTest::newRow("uniqueName6") << "FGM" << std::vector{"A", "B", "C"} << "FGM"; + QTest::newRow("uniqueName7") << "FGM" << std::vector{"fGm", "FGm1", "Fgm2"} << "FGM3"; + QTest::newRow("uniqueName8") << "" << std::vector{"A", "B", "C"} << "1"; + QTest::newRow("uniqueName9") << "24" << std::vector{"A", "B", "C"} << "1"; +} + +void TestStringUtils::testUniqueName() +{ + QFETCH(QString, defaultName); + QFETCH(std::vector, forbiddenNames); + QFETCH(QString, expectedName); + + auto result = StringUtils::uniqueName(defaultName, forbiddenNames); + QCOMPARE(result, expectedName); +} + +QTEST_MAIN(TestStringUtils) +#include "TestStringUtils.moc" diff --git a/core/tests/meson.build b/core/tests/meson.build index 7f15f45..83328c6 100644 --- a/core/tests/meson.build +++ b/core/tests/meson.build @@ -1,6 +1,7 @@ tests = [ + [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'], [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'], [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'], [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],