diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 02ebe92de2..918ced0a6f 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -261,6 +261,7 @@ SET(FreeCADApp_CPP_SRCS ComplexGeoData.cpp ComplexGeoDataPyImp.cpp Enumeration.cpp + IndexedName.cpp Material.cpp MaterialPyImp.cpp Metadata.cpp @@ -277,6 +278,7 @@ SET(FreeCADApp_HPP_SRCS ColorModel.h ComplexGeoData.h Enumeration.h + IndexedName.h Material.h Metadata.h ) diff --git a/src/App/IndexedName.cpp b/src/App/IndexedName.cpp new file mode 100644 index 0000000000..e4bc448d4d --- /dev/null +++ b/src/App/IndexedName.cpp @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/**************************************************************************** + * Copyright (c) 2022 Zheng, Lei (realthunder) * + * * + * 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 * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +#endif + +#include + +#include "IndexedName.h" + +using namespace Data; + +struct ByteArray +{ + ByteArray(const QByteArray& b) + :bytes(b) + {} + + ByteArray(const ByteArray& other) + :bytes(other.bytes) + {} + + ByteArray(ByteArray&& other) + :bytes(std::move(other.bytes)) + {} + + void mutate() const + { + QByteArray copy; + copy.append(bytes.constData(), bytes.size()); + bytes = copy; + } + + bool operator==(const ByteArray& other) const { + return bytes == other.bytes; + } + + mutable QByteArray bytes; +}; + +struct ByteArrayHasher +{ + std::size_t operator()(const ByteArray& bytes) const + { + return qHash(bytes.bytes); + } + + std::size_t operator()(const QByteArray& bytes) const + { + return qHash(bytes); + } +}; + +void IndexedName::set( + const char* name, + int len, + const std::vector& types, + bool allowOthers) +{ + static std::unordered_set NameSet; + + if (len < 0) + len = static_cast(std::strlen(name)); + int i; + for (i = len - 1; i >= 0; --i) { + if (name[i] < '0' || name[i]>'9') + break; + } + ++i; + this->index = std::atoi(name + i); + + for (int j = 0; j < i; ++j) { + if (name[j] == '_' + || (name[j] >= 'a' && name[j] <= 'z') + || (name[j] >= 'A' && name[j] <= 'Z')) + continue; + this->type = ""; + return; + } + + for (const char* type : types) { + int j = 0; + for (const char* n = name, *t = type; *n; ++n) { + if (*n != *t || j >= i) + break; + ++j; + ++t; + if (!*t) { + this->type = type; + return; + } + } + } + + if (allowOthers) { + auto res = NameSet.insert(QByteArray::fromRawData(name, i)); + if (res.second) + res.first->mutate(); + this->type = res.first->bytes.constData(); + } + else + this->type = ""; +} diff --git a/src/App/IndexedName.h b/src/App/IndexedName.h new file mode 100644 index 0000000000..3e64882934 --- /dev/null +++ b/src/App/IndexedName.h @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/**************************************************************************** + * Copyright (c) 2022 Zheng, Lei (realthunder) * + * * + * 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 * + * . * + * * + ***************************************************************************/ + + +#ifndef _IndexedName_h_ +#define _IndexedName_h_ + +#include +#include +#include +#include +#include + +#include + +#include "FCGlobal.h" + + +namespace Data +{ + +class AppExport IndexedName { +public: + explicit IndexedName(const char *name = nullptr, int _index = 0) + : index(0) + { + if (!name) + this->type = ""; + else { + set(name); + if (_index) + this->index = _index; + } + } + + IndexedName(const char *name, + const std::vector & types, + bool allowOthers=true) + { + set(name, -1, types, allowOthers); + } + + explicit IndexedName(const QByteArray & data) + { + set(data.constData(), data.size()); + } + + IndexedName(const IndexedName &other) + : type(other.type), index(other.index) + {} + + static IndexedName fromConst(const char *name, int index) { + IndexedName res; + res.type = name; + res.index = index; + return res; + } + + IndexedName & operator=(const IndexedName & other) + { + this->index = other.index; + this->type = other.type; + return *this; + } + + friend std::ostream & operator<<(std::ostream & s, const IndexedName & e) + { + s << e.type; + if (e.index > 0) + s << e.index; + return s; + } + + bool operator==(const IndexedName & other) const + { + return this->index == other.index + && (this->type == other.type + || std::strcmp(this->type, other.type)==0); + } + + IndexedName & operator+=(int offset) + { + this->index += offset; + assert(this->index >= 0); + return *this; + } + + IndexedName & operator++() + { + ++this->index; + return *this; + } + + IndexedName & operator--() + { + --this->index; + assert(this->index >= 0); + return *this; + } + + bool operator!=(const IndexedName & other) const + { + return !(this->operator==(other)); + } + + const char * toString(std::string & s) const + { + // Note! s is not cleared on purpose. + std::size_t offset = s.size(); + s += this->type; + if (this->index > 0) + s += std::to_string(this->index); + return s.c_str() + offset; + } + + int compare(const IndexedName & other) const + { + int res = std::strcmp(this->type, other.type); + if (res) + return res; + if (this->index < other.index) + return -1; + if (this->index > other.index) + return 1; + return 0; + } + + bool operator<(const IndexedName & other) const + { + return compare(other) < 0; + } + + char operator[](int index) const + { + return this->type[index]; + } + + const char * getType() const { return this->type; } + + int getIndex() const { return this->index; } + + void setIndex(int index) { assert(index>=0); this->index = index; } + + bool isNull() const { return !this->type[0]; } + + explicit operator bool() const { return !isNull(); } + +protected: + void set(const char *, + int len = -1, + const std::vector &types = {}, + bool allowOthers = true); + +private: + const char * type; + int index; +}; + +} + +#endif _IndexedName_h_