Files
create/src/Base/UniqueNameManager.h
Benjamin Bræstrup Sayoc 6f619b5f1c Base: remove unneeded includes
2025-02-18 11:07:57 -06:00

154 lines
6.7 KiB
C++

/***************************************************************************
* Copyright (c) 2024 Kevin Martin <kpmartin@papertrail.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef SRC_BASE_UNIQUENAMEMANAGER_H_
#define SRC_BASE_UNIQUENAMEMANAGER_H_
#ifndef FC_GLOBAL_H
#include <FCGlobal.h>
#endif
#include <vector>
#include <string>
#include <set>
#include <map>
#include <utility> // Forward declares std::tuple
// ----------------------------------------------------------------------------
namespace Base
{
/// @brief
/// This class acts as a multiset of strings with the capability of generating
/// other names that are not in the collection. Unique names are generated
/// by inserting digits into a base name. Normally these are appended to the
/// base name, but a derived class can override getNameSuffixStartPosition
/// to place the digits elsewhere in the base name.
// This class does not support copy-/move-construction
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
class BaseExport UniqueNameManager
{
protected:
// This method returns a reverse const iterator on name that references the position
// of the start of the suffix (or name.crbegin() if no suffix).
// It must return the same suffix length (name.crbegin() - returnValue) for both
// unique names (one containing digits) and the corresponding base name (with no digits).
virtual std::string::const_reverse_iterator
getNameSuffixStartPosition(const std::string& name) const
{
return name.crbegin();
}
private:
class PiecewiseSparseIntegerSet
{
public:
PiecewiseSparseIntegerSet() = default;
private:
// Each pair being <lowest, count> represents the span of integers from lowest to
// (lowest+count-1) inclusive
using SpanType = std::pair<unsigned int, unsigned int>;
// This span Comparer class is analogous to std::less and treats overlapping spans as being
// neither greater nor less than each other
class Comparer
{
public:
bool operator()(SpanType lhs, SpanType rhs) const
{
// The equality case here is when lhs is below and directly adjacent to rhs.
return lhs.second + lhs.first <= rhs.first;
}
};
// spans is the set of spans. Adjacent spans are coalesced so there are always gaps between
// the entries.
std::set<SpanType, Comparer> spans;
public:
void add(unsigned int value);
void remove(unsigned int value);
bool contains(unsigned int value) const;
bool empty() const
{
return spans.empty();
}
void clear()
{
spans.clear();
}
unsigned int next() const
{
if (spans.empty()) {
return 0;
}
auto last = spans.end();
--last;
return last->first + last->second;
}
};
// Keyed as uniqueSeeds[baseName][digitCount][digitValue] iff that seed is taken.
// We need the double-indexing so that Name01 and Name001 can both be indexed, although we only
// ever allocate off the longest for each name i.e. uniqueSeeds[baseName].size()-1 digits.
std::map<std::string, std::vector<PiecewiseSparseIntegerSet>> uniqueSeeds;
// Counts of inserted strings that have duplicates, i.e. more than one instance in the
// collection. This does not contain entries for singleton names.
std::map<std::string, unsigned int> duplicateCounts;
/// @brief Break a uniquified name into its parts
/// @param name The name to break up
/// @return a tuple(basePrefix, nameSuffix, uniqueDigitCount, uniqueDigitsValue);
/// The two latter values will be (0,0) if name is a base name without uniquifying digits.
std::tuple<std::string, std::string, unsigned int, unsigned int>
decomposeName(const std::string& name) const;
public:
UniqueNameManager() = default;
virtual ~UniqueNameManager() = default;
/// Check if two names are unique forms of the same base name
bool sameBaseName(const std::string& first, const std::string& second) const;
/// Register a name in the collection.
/// This collection acts as a multiset, so multiple registrations of the same
/// name are counted, and an equal number of removals is necessary to fully
/// remove the name, allowing it to be returned by makeUniqueName
void addExactName(const std::string& name);
/// Using the given modelName as a model, generate a name that is not already
/// in the collection of names. The model name may already contain uniquifying digits
/// which will be stripped and replaced with other digits as needed.
std::string makeUniqueName(const std::string& modelName, std::size_t minDigits = 0) const;
/// Remove a registered name so it can be generated again. If the name was added
/// several times this decrements the count, and the name is only fully removed when
/// the count of removes equals or exceeds the count of adds.
void removeExactName(const std::string& name);
/// Test if the given name is already in the collection
bool containsName(const std::string& name) const;
/// @brief Empty (clear) out the contents from this collection
void clear()
{
uniqueSeeds.clear();
duplicateCounts.clear();
}
};
} // namespace Base
#endif // SRC_BASE_UNIQUENAMEMANAGER_H_