[Toponaming] create ElementMap class (#9175)
* Copypaste ElementMap * Add MappedNameRef * Fix missing include * Copypaste `findTagInElementName` * fix error introduced _somewhere_ * refactor toponaming constants * Move `findTagInElementName` in `MappedName` * reintroduce workaround to compile ElementMap * Added missing functions copied from complexgeodata * fix last compile errors, reorder and format files * remove recursive refs to ComplexGeoData * Add more comments * fixed comments and added tests * added FIXME, make functions private, misc fixes * Move static functions from complexGeoData to PostfixStringReferences. Rename to ElementNamingUtils * Fix broken includes due to previous change * Revert constants from string to const char* * added childmap tests and made hasher public * Make functions private * Added remaining tests * removed bool return from `erase` functions * fix missing appexport Co-authored-by: John Dupuy <jdupuy98@gmail.com>
This commit is contained in:
@@ -266,6 +266,7 @@ SET(FreeCADApp_CPP_SRCS
|
||||
ColorModel.cpp
|
||||
ComplexGeoData.cpp
|
||||
ComplexGeoDataPyImp.cpp
|
||||
ElementMap.cpp
|
||||
Enumeration.cpp
|
||||
IndexedName.cpp
|
||||
MappedElement.cpp
|
||||
@@ -274,6 +275,7 @@ SET(FreeCADApp_CPP_SRCS
|
||||
MaterialPyImp.cpp
|
||||
Metadata.cpp
|
||||
MetadataPyImp.cpp
|
||||
ElementNamingUtils.cpp
|
||||
StringHasher.cpp
|
||||
StringHasherPyImp.cpp
|
||||
StringIDPyImp.cpp
|
||||
@@ -295,6 +297,7 @@ SET(FreeCADApp_HPP_SRCS
|
||||
MappedElement.h
|
||||
Material.h
|
||||
Metadata.h
|
||||
ElementNamingUtils.h
|
||||
StringHasher.h
|
||||
)
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
# include <cstdlib>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include "ComplexGeoData.h"
|
||||
|
||||
#include <Base/BoundBox.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Rotation.h>
|
||||
@@ -166,102 +166,3 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d&) const
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::elementMapPrefix() {
|
||||
static std::string prefix(";");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
const char *ComplexGeoData::isMappedElement(const char *name) {
|
||||
if(name && boost::starts_with(name,elementMapPrefix()))
|
||||
return name+elementMapPrefix().size();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::newElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name)
|
||||
return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,dot-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::oldElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name)
|
||||
return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,c-name)+(dot+1);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string ComplexGeoData::noElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
auto element = findElementName(name);
|
||||
if(element)
|
||||
return std::string(name,element-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *ComplexGeoData::findElementName(const char *subname) {
|
||||
if(!subname || !subname[0] || isMappedElement(subname))
|
||||
return subname;
|
||||
const char *dot = strrchr(subname,'.');
|
||||
if(!dot)
|
||||
return subname;
|
||||
const char *element = dot+1;
|
||||
if(dot==subname || isMappedElement(element))
|
||||
return element;
|
||||
for(--dot;dot!=subname;--dot) {
|
||||
if(*dot == '.') {
|
||||
++dot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(dot))
|
||||
return dot;
|
||||
return element;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::tagPostfix() {
|
||||
static std::string postfix(elementMapPrefix() + ":T");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::indexPostfix() {
|
||||
static std::string postfix(elementMapPrefix() + ":I");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string &ComplexGeoData::missingPrefix() {
|
||||
static std::string prefix("?");
|
||||
return prefix;
|
||||
}
|
||||
|
||||
bool ComplexGeoData::hasMissingElement(const char *subname) {
|
||||
if(!subname)
|
||||
return false;
|
||||
auto dot = strrchr(subname,'.');
|
||||
if(dot)
|
||||
subname = dot+1;
|
||||
return boost::starts_with(subname,missingPrefix());
|
||||
}
|
||||
|
||||
@@ -164,41 +164,6 @@ public:
|
||||
virtual bool getCenterOfGravity(Base::Vector3d& center) const;
|
||||
//@}
|
||||
|
||||
/** @name Element name mapping */
|
||||
//@{
|
||||
/// Special prefix to mark the beginning of a mapped sub-element name
|
||||
static const std::string &elementMapPrefix();
|
||||
/// Special postfix to mark the following tag
|
||||
static const std::string &tagPostfix();
|
||||
/// Special postfix to mark the index of an array element
|
||||
static const std::string &indexPostfix();
|
||||
/// Special prefix to mark a missing element
|
||||
static const std::string &missingPrefix();
|
||||
/// Check if a subname contains missing element
|
||||
static bool hasMissingElement(const char *subname);
|
||||
/** Check if the name starts with elementMapPrefix()
|
||||
*
|
||||
* @param name: input name
|
||||
* @return Returns the name stripped with elementMapPrefix(), or 0 if not
|
||||
* start with the prefix
|
||||
*/
|
||||
static const char *isMappedElement(const char *name);
|
||||
|
||||
/// Strip out the trailing element name if there is mapped element name precedes it.
|
||||
static std::string newElementName(const char *name);
|
||||
/// Strip out the mapped element name if there is one.
|
||||
static std::string oldElementName(const char *name);
|
||||
/// Strip out the old and new element name if there is one.
|
||||
static std::string noElementName(const char *name);
|
||||
|
||||
/// Find the start of an element name in a subname
|
||||
static const char *findElementName(const char *subname);
|
||||
|
||||
static inline const char *hasMappedElementName(const char *subname) {
|
||||
return isMappedElement(findElementName(subname));
|
||||
}
|
||||
//@}
|
||||
|
||||
protected:
|
||||
|
||||
/// from local to outside
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <Base/Writer.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ComplexGeoData.h"
|
||||
#include "ElementNamingUtils.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObject.h"
|
||||
#include "DocumentObjectExtension.h"
|
||||
@@ -1094,7 +1094,7 @@ DocumentObject *DocumentObject::resolve(const char *subname,
|
||||
// following it. So finding the last dot will give us the end of the last
|
||||
// object name.
|
||||
const char *dot=nullptr;
|
||||
if(Data::ComplexGeoData::isMappedElement(subname) ||
|
||||
if(Data::isMappedElement(subname) ||
|
||||
!(dot=strrchr(subname,'.')) ||
|
||||
dot == subname)
|
||||
{
|
||||
@@ -1117,7 +1117,7 @@ DocumentObject *DocumentObject::resolve(const char *subname,
|
||||
if(!elementMapChecked) {
|
||||
elementMapChecked = true;
|
||||
const char *sub = dot==subname?dot:dot+1;
|
||||
if(Data::ComplexGeoData::isMappedElement(sub)) {
|
||||
if(Data::isMappedElement(sub)) {
|
||||
lastDot = dot;
|
||||
if(dot==subname)
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ComplexGeoData.h"
|
||||
#include "ElementNamingUtils.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObserver.h"
|
||||
#include "GeoFeature.h"
|
||||
@@ -353,11 +353,11 @@ const std::string &SubObjectT::getSubName() const {
|
||||
}
|
||||
|
||||
std::string SubObjectT::getSubNameNoElement() const {
|
||||
return Data::ComplexGeoData::noElementName(subname.c_str());
|
||||
return Data::noElementName(subname.c_str());
|
||||
}
|
||||
|
||||
const char *SubObjectT::getElementName() const {
|
||||
return Data::ComplexGeoData::findElementName(subname.c_str());
|
||||
return Data::findElementName(subname.c_str());
|
||||
}
|
||||
|
||||
std::string SubObjectT::getNewElementName() const {
|
||||
|
||||
1158
src/App/ElementMap.cpp
Normal file
1158
src/App/ElementMap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,22 +27,264 @@
|
||||
#define DATA_ELEMENTMAP_H
|
||||
|
||||
#include "FCGlobal.h"
|
||||
#include "IndexedName.h"
|
||||
|
||||
namespace Data {
|
||||
#include "Application.h"
|
||||
#include "MappedElement.h"
|
||||
#include "StringHasher.h"
|
||||
|
||||
static constexpr const char *POSTFIX_TAG = ";:H";
|
||||
static constexpr const char *POSTFIX_DECIMAL_TAG = ";:T";
|
||||
static constexpr const char *POSTFIX_EXTERNAL_TAG = ";:X";
|
||||
static constexpr const char *POSTFIX_CHILD = ";:C";
|
||||
static constexpr const char *POSTFIX_INDEX = ";:I";
|
||||
static constexpr const char *POSTFIX_UPPER = ";:U";
|
||||
static constexpr const char *POSTFIX_LOWER = ";:L";
|
||||
static constexpr const char *POSTFIX_MOD = ";:M";
|
||||
static constexpr const char *POSTFIX_GEN = ";:G";
|
||||
static constexpr const char *POSTFIX_MODGEN = ";:MG";
|
||||
static constexpr const char *POSTFIX_DUPLICATE = ";D";
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
} // namespace data
|
||||
|
||||
#endif // DATA_ELEMENTMAP_H
|
||||
namespace Data
|
||||
{
|
||||
|
||||
class ElementMap;
|
||||
typedef std::shared_ptr<ElementMap> ElementMapPtr;
|
||||
|
||||
/* This class provides for ComplexGeoData's ability to provide proper naming.
|
||||
* Specifically, ComplexGeoData uses this class for it's `_id` property.
|
||||
* Most of the operations work with the `indexedNames` and `mappedNames` maps.
|
||||
* `indexedNames` maps a string to both a name queue and children.
|
||||
* each of those children store an IndexedName, offset details, postfix, ids, and
|
||||
* possibly a recursive elementmap
|
||||
* `mappedNames` maps a MappedName to a specific IndexedName.
|
||||
*/
|
||||
class AppExport ElementMap: public std::enable_shared_from_this<ElementMap> //TODO can remove shared_from_this?
|
||||
{
|
||||
public:
|
||||
/** Default constructor: hooks internal functions to \c signalSaveDocument and
|
||||
* \c signalStartRestoreDocument. This is related to the save and restore process
|
||||
* of the map.
|
||||
*/
|
||||
ElementMap();
|
||||
|
||||
/** Ensures that naming is properly assigned. It then marks as "used" all the StringID
|
||||
* that are used to make up this particular map and are stored in the hasher passed
|
||||
* as a parameter. Finally do this recursively for all childEelementMaps as well.
|
||||
*
|
||||
* @param hasher where all the StringID needed to build the map are stored.
|
||||
*/
|
||||
// FIXME this should be made part of \c save, to achieve symmetry with the restore method
|
||||
void beforeSave(const ::App::StringHasherRef& hasher) const;
|
||||
|
||||
/** Serialize this map. Calls \c collectChildMaps to get \c childMapSet and
|
||||
* \c postfixMap, then calls the other (private) save function with those parameters.
|
||||
* @param s: serialized stream
|
||||
*/
|
||||
void save(std::ostream& s) const;
|
||||
|
||||
/** Deserialize and restore this map. This function restores \c childMaps and
|
||||
* \c postfixes from the stream, then calls the other (private) restore function with those
|
||||
* parameters.
|
||||
* @param hasher: where all the StringIDs are stored
|
||||
* @param s: stream to deserialize
|
||||
*/
|
||||
ElementMapPtr restore(::App::StringHasherRef hasher, std::istream& s);
|
||||
|
||||
|
||||
/** Add a sub-element name mapping.
|
||||
*
|
||||
* @param element: the original \c Type + \c Index element name
|
||||
* @param name: the mapped sub-element name. May or may not start with
|
||||
* elementMapPrefix().
|
||||
* @param sid: in case you use a hasher to hash the element name, pass in
|
||||
* the string id reference using this parameter. You can have more than one
|
||||
* string id associated with the same name.
|
||||
* @param overwrite: if true, it will overwrite existing names
|
||||
*
|
||||
* @return Returns the stored mapped element name.
|
||||
*
|
||||
* An element can have multiple mapped names. However, a name can only be
|
||||
* mapped to one element
|
||||
*
|
||||
* Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access,
|
||||
* now you must pass in `long masterTag` explicitly.
|
||||
*/
|
||||
MappedName setElementName(const IndexedName& element,
|
||||
const MappedName& name,
|
||||
long masterTag,
|
||||
const ElementIDRefs* sid = nullptr,
|
||||
bool overwrite = false);
|
||||
|
||||
/* Generates a new MappedName from the current details.
|
||||
*
|
||||
* The result is streamed to `ss` and stored in `name`.
|
||||
*
|
||||
* Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access,
|
||||
* now you must pass in `long masterTag` explicitly.
|
||||
*/
|
||||
void encodeElementName(char element_type,
|
||||
MappedName& name,
|
||||
std::ostringstream& ss,
|
||||
ElementIDRefs* sids,
|
||||
long masterTag,
|
||||
const char* postfix = 0,
|
||||
long tag = 0,
|
||||
bool forceTag = false) const;
|
||||
|
||||
/// Remove \c name from the map
|
||||
void erase(const MappedName& name);
|
||||
|
||||
/// Remove \c idx and all the MappedNames associated with it
|
||||
void erase(const IndexedName& idx);
|
||||
|
||||
unsigned long size() const;
|
||||
|
||||
bool empty() const;
|
||||
|
||||
IndexedName find(const MappedName& name, ElementIDRefs* sids = nullptr) const;
|
||||
|
||||
MappedName find(const IndexedName& idx, ElementIDRefs* sids = nullptr) const;
|
||||
|
||||
std::vector<std::pair<MappedName, ElementIDRefs>> findAll(const IndexedName& idx) const;
|
||||
|
||||
// prefix searching is disabled, as TopoShape::getRelatedElement() is
|
||||
// deprecated in favor of GeoFeature::getRelatedElement(). Besides, there
|
||||
// is efficient way to support child element map if we were to implement
|
||||
// prefix search.
|
||||
#if 0
|
||||
std::vector<MappedElement> findAllStartsWith(const char *prefix) const;
|
||||
#endif
|
||||
|
||||
bool hasChildElementMap() const;
|
||||
|
||||
/* Ensures that for each IndexedName mapped to IndexedElements, that
|
||||
* each child is properly hashed (cached).
|
||||
*
|
||||
* Note: the original proc was in the context of ComplexGeoData, which provided `Tag` access,
|
||||
* now you must pass in `long masterTag` explicitly.
|
||||
*/
|
||||
void hashChildMaps(long masterTag);
|
||||
|
||||
struct AppExport MappedChildElements
|
||||
{
|
||||
IndexedName indexedName;
|
||||
int count;
|
||||
int offset;
|
||||
long tag;
|
||||
ElementMapPtr elementMap;
|
||||
QByteArray postfix;
|
||||
ElementIDRefs sids;
|
||||
|
||||
// prefix() has been moved to PostfixStringReferences.h
|
||||
};
|
||||
|
||||
/* Note: the original addChildElements passed `ComplexGeoData& master` for getting the `Tag`,
|
||||
* now it just passes `long masterTag`.*/
|
||||
void addChildElements(long masterTag, const std::vector<MappedChildElements>& children);
|
||||
|
||||
std::vector<MappedChildElements> getChildElements() const;
|
||||
|
||||
std::vector<MappedElement> getAll() const;
|
||||
|
||||
private:
|
||||
/** Serialize this map
|
||||
* @param s: serialized stream
|
||||
* @param childMapSet: where all child element maps are stored
|
||||
* @param postfixMap. where all postfixes are stored
|
||||
*/
|
||||
void save(std::ostream& s, int index, const std::map<const ElementMap*, int>& childMapSet,
|
||||
const std::map<QByteArray, int>& postfixMap) const;
|
||||
|
||||
/** Deserialize and restore this map.
|
||||
* @param hasher: where all the StringIDs are stored
|
||||
* @param s: stream to deserialize
|
||||
* @param childMaps: where all child element maps are stored
|
||||
* @param postfixes. where all postfixes are stored
|
||||
*/
|
||||
ElementMapPtr restore(::App::StringHasherRef hasher, std::istream& s,
|
||||
std::vector<ElementMapPtr>& childMaps,
|
||||
const std::vector<std::string>& postfixes);
|
||||
|
||||
/** Associate the MappedName \c name with the IndexedName \c idx.
|
||||
* @param name: the name to add
|
||||
* @param idx: the indexed name that \c name will be bound to
|
||||
* @param sids: where StringIDs that make up the name are stored
|
||||
* @param overwrite: if true, all the names associated with \c idx will be discarded
|
||||
* @param existing: out variable: if not overwriting, and \c name is already
|
||||
* associated with another indexedName, set \c existing to that indexedname
|
||||
* @return the name just added, or an empty name if it wasn't added.
|
||||
*/
|
||||
MappedName addName(MappedName& name, const IndexedName& idx, const ElementIDRefs& sids,
|
||||
bool overwrite, IndexedName* existing);
|
||||
|
||||
/** Utility function that adds \c postfix to \c postfixMap, and to \c postfixes
|
||||
* if it was not present in the map.
|
||||
*/
|
||||
static void addPostfix(const QByteArray& postfix, std::map<QByteArray, int>& postfixMap,
|
||||
std::vector<QByteArray>& postfixes);
|
||||
|
||||
/* Note: the original proc passed `ComplexGeoData& master` for getting the `Tag`,
|
||||
* now it just passes `long masterTag`.*/
|
||||
virtual MappedName renameDuplicateElement(int index, const IndexedName& element,
|
||||
const IndexedName& element2, const MappedName& name,
|
||||
ElementIDRefs& sids, long masterTag);
|
||||
|
||||
/** Convenience method to hash the main element name
|
||||
*
|
||||
* @param name: main element name
|
||||
* @param sid: store any output string ID references
|
||||
* @return the hashed element name;
|
||||
*/
|
||||
MappedName hashElementName(const MappedName& name, ElementIDRefs& sids) const;
|
||||
|
||||
/// Reverse hashElementName()
|
||||
MappedName dehashElementName(const MappedName& name) const;
|
||||
|
||||
//FIXME duplicate code? as in copy/paste
|
||||
const MappedNameRef* findMappedRef(const IndexedName& idx) const;
|
||||
MappedNameRef* findMappedRef(const IndexedName& idx);
|
||||
|
||||
MappedNameRef& mappedRef(const IndexedName& idx);
|
||||
|
||||
void collectChildMaps(std::map<const ElementMap*, int>& childMapSet,
|
||||
std::vector<const ElementMap*>& childMaps,
|
||||
std::map<QByteArray, int>& postfixMap,
|
||||
std::vector<QByteArray>& postfixes) const;
|
||||
|
||||
struct CStringComp
|
||||
{
|
||||
public:
|
||||
bool operator()(const char* str1, const char* str2) const
|
||||
{
|
||||
return std::strcmp(str1, str2) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct IndexedElements
|
||||
{
|
||||
std::deque<MappedNameRef> names;
|
||||
std::map<int, MappedChildElements> children;
|
||||
};
|
||||
|
||||
std::map<const char*, IndexedElements, CStringComp> indexedNames;
|
||||
|
||||
std::map<MappedName, IndexedName, std::less<MappedName>> mappedNames;
|
||||
|
||||
struct ChildMapInfo
|
||||
{
|
||||
int index = 0;
|
||||
MappedChildElements* childMap = nullptr;
|
||||
std::map<ElementMap*, int> mapIndices;
|
||||
};
|
||||
|
||||
QHash<QByteArray, ChildMapInfo> childElements;
|
||||
std::size_t childElementSize = 0;
|
||||
|
||||
mutable unsigned _id = 0;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
/// String hasher for element name shortening
|
||||
App::StringHasherRef hasher;
|
||||
};
|
||||
|
||||
|
||||
}// namespace Data
|
||||
|
||||
#endif// DATA_ELEMENTMAP_H
|
||||
|
||||
88
src/App/ElementNamingUtils.cpp
Normal file
88
src/App/ElementNamingUtils.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "ElementNamingUtils.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
const char *Data::isMappedElement(const char *name) {
|
||||
if(name && boost::starts_with(name, ELEMENT_MAP_PREFIX))
|
||||
return name + ELEMENT_MAP_PREFIX_SIZE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string Data::newElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name)
|
||||
return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,dot-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string Data::oldElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
const char *dot = strrchr(name,'.');
|
||||
if(!dot || dot==name)
|
||||
return name;
|
||||
const char *c = dot-1;
|
||||
for(;c!=name;--c) {
|
||||
if(*c == '.') {
|
||||
++c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(c))
|
||||
return std::string(name,c-name)+(dot+1);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string Data::noElementName(const char *name) {
|
||||
if(!name)
|
||||
return std::string();
|
||||
auto element = findElementName(name);
|
||||
if(element)
|
||||
return std::string(name,element-name);
|
||||
return name;
|
||||
}
|
||||
|
||||
const char *Data::findElementName(const char *subname) {
|
||||
if(!subname || !subname[0] || isMappedElement(subname))
|
||||
return subname;
|
||||
const char *dot = strrchr(subname,'.');
|
||||
if(!dot)
|
||||
return subname;
|
||||
const char *element = dot+1;
|
||||
if(dot==subname || isMappedElement(element))
|
||||
return element;
|
||||
for(--dot;dot!=subname;--dot) {
|
||||
if(*dot == '.') {
|
||||
++dot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isMappedElement(dot))
|
||||
return dot;
|
||||
return element;
|
||||
}
|
||||
|
||||
bool Data::hasMissingElement(const char *subname) {
|
||||
if(!subname)
|
||||
return false;
|
||||
auto dot = strrchr(subname,'.');
|
||||
if(dot)
|
||||
subname = dot+1;
|
||||
return boost::starts_with(subname, MISSING_PREFIX);
|
||||
}
|
||||
|
||||
const char *Data::hasMappedElementName(const char *subname) {
|
||||
return isMappedElement(findElementName(subname));
|
||||
}
|
||||
66
src/App/ElementNamingUtils.h
Normal file
66
src/App/ElementNamingUtils.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "FCGlobal.h"
|
||||
|
||||
|
||||
namespace Data
|
||||
{
|
||||
|
||||
/// Special prefix to mark the beginning of a mapped sub-element name
|
||||
constexpr const char* ELEMENT_MAP_PREFIX = ";";
|
||||
constexpr size_t ELEMENT_MAP_PREFIX_SIZE = 1;
|
||||
|
||||
/// Special prefix to mark a missing element
|
||||
constexpr const char* MISSING_PREFIX = "?";
|
||||
|
||||
// IMPORTANT: For all the constants below, the semicolon ";"
|
||||
// at the start is ELEMENT_MAP_PREFIX
|
||||
|
||||
constexpr const char* MAPPED_CHILD_ELEMENTS_PREFIX = ";:R";
|
||||
|
||||
/// Special postfix to mark the following tag
|
||||
constexpr const char* POSTFIX_TAG = ";:H";
|
||||
constexpr size_t POSTFIX_TAG_SIZE = 3;
|
||||
|
||||
constexpr const char* POSTFIX_DECIMAL_TAG = ";:T";
|
||||
constexpr const char* POSTFIX_EXTERNAL_TAG = ";:X";
|
||||
constexpr const char* POSTFIX_CHILD = ";:C";
|
||||
|
||||
/// Special postfix to mark the index of an array element
|
||||
constexpr const char* POSTFIX_INDEX = ";:I";
|
||||
constexpr const char* POSTFIX_UPPER = ";:U";
|
||||
constexpr const char* POSTFIX_LOWER = ";:L";
|
||||
constexpr const char* POSTFIX_MOD = ";:M";
|
||||
constexpr const char* POSTFIX_GEN = ";:G";
|
||||
constexpr const char* POSTFIX_MODGEN = ";:MG";
|
||||
constexpr const char* POSTFIX_DUPLICATE = ";D";
|
||||
|
||||
|
||||
/// Check if a subname contains missing element
|
||||
AppExport bool hasMissingElement(const char *subname);
|
||||
|
||||
/** Check if the name starts with elementMapPrefix()
|
||||
*
|
||||
* @param name: input name
|
||||
* @return Returns the name stripped with elementMapPrefix(), or 0 if not
|
||||
* start with the prefix
|
||||
*/
|
||||
AppExport const char *isMappedElement(const char *name);
|
||||
|
||||
/// Strip out the trailing element name if there is mapped element name preceeds it.
|
||||
AppExport std::string newElementName(const char *name);
|
||||
|
||||
/// Strip out the mapped element name if there is one.
|
||||
AppExport std::string oldElementName(const char *name);
|
||||
|
||||
/// Strip out the old and new element name if there is one.
|
||||
AppExport std::string noElementName(const char *name);
|
||||
|
||||
/// Find the start of an element name in a subname
|
||||
AppExport const char *findElementName(const char *subname);
|
||||
|
||||
AppExport const char *hasMappedElementName(const char *subname);
|
||||
|
||||
|
||||
}// namespace Data
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "GeoFeature.h"
|
||||
#include "GeoFeatureGroupExtension.h"
|
||||
#include "ComplexGeoData.h"
|
||||
#include "ElementNamingUtils.h"
|
||||
|
||||
|
||||
using namespace App;
|
||||
@@ -101,7 +101,7 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
|
||||
return nullptr;
|
||||
if(!subname)
|
||||
subname = "";
|
||||
const char *element = Data::ComplexGeoData::findElementName(subname);
|
||||
const char *element = Data::findElementName(subname);
|
||||
if(_element) *_element = element;
|
||||
auto sobj = obj->getSubObject(subname);
|
||||
if(!sobj)
|
||||
@@ -114,7 +114,7 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
|
||||
return nullptr;
|
||||
if(!element || !element[0]) {
|
||||
if(append)
|
||||
elementName.second = Data::ComplexGeoData::oldElementName(subname);
|
||||
elementName.second = Data::oldElementName(subname);
|
||||
return sobj;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ DocumentObject *GeoFeature::resolveElement(DocumentObject *obj, const char *subn
|
||||
if(!append)
|
||||
elementName.second = element;
|
||||
else
|
||||
elementName.second = Data::ComplexGeoData::oldElementName(subname);
|
||||
elementName.second = Data::oldElementName(subname);
|
||||
return sobj;
|
||||
}
|
||||
if(!append)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <Base/Uuid.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ComplexGeoData.h"
|
||||
#include "ElementNamingUtils.h"
|
||||
#include "ComplexGeoDataPy.h"
|
||||
#include "Document.h"
|
||||
#include "DocumentObserver.h"
|
||||
@@ -1051,7 +1051,7 @@ DocumentObject *LinkBaseExtension::getLink(int depth) const{
|
||||
}
|
||||
|
||||
int LinkBaseExtension::getArrayIndex(const char *subname, const char **psubname) {
|
||||
if(!subname || Data::ComplexGeoData::isMappedElement(subname))
|
||||
if(!subname || Data::isMappedElement(subname))
|
||||
return -1;
|
||||
const char *dot = strchr(subname,'.');
|
||||
if(!dot) dot= subname+strlen(subname);
|
||||
@@ -1073,7 +1073,7 @@ int LinkBaseExtension::getArrayIndex(const char *subname, const char **psubname)
|
||||
}
|
||||
|
||||
int LinkBaseExtension::getElementIndex(const char *subname, const char **psubname) const {
|
||||
if(!subname || Data::ComplexGeoData::isMappedElement(subname))
|
||||
if(!subname || Data::isMappedElement(subname))
|
||||
return -1;
|
||||
int idx = -1;
|
||||
const char *dot = strchr(subname,'.');
|
||||
@@ -1313,7 +1313,7 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
|
||||
return true;
|
||||
ret = elements[idx]->getSubObject(subname,pyObj,mat,true,depth+1);
|
||||
// do not resolve the link if this element is the last referenced object
|
||||
if(!subname || Data::ComplexGeoData::isMappedElement(subname) || !strchr(subname,'.'))
|
||||
if(!subname || Data::isMappedElement(subname) || !strchr(subname,'.'))
|
||||
ret = elements[idx];
|
||||
return true;
|
||||
}
|
||||
@@ -1381,7 +1381,7 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
|
||||
std::string postfix;
|
||||
if(ret) {
|
||||
// do not resolve the link if we are the last referenced object
|
||||
if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.')) {
|
||||
if(subname && !Data::isMappedElement(subname) && strchr(subname,'.')) {
|
||||
if(mat)
|
||||
*mat = matNext;
|
||||
}
|
||||
@@ -1394,7 +1394,7 @@ bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *
|
||||
}
|
||||
else {
|
||||
if(idx) {
|
||||
postfix = Data::ComplexGeoData::indexPostfix();
|
||||
postfix = Data::POSTFIX_INDEX;
|
||||
postfix += std::to_string(idx);
|
||||
}
|
||||
if(mat)
|
||||
@@ -1488,7 +1488,7 @@ void LinkBaseExtension::parseSubName() const {
|
||||
}
|
||||
const auto &subs = xlink->getSubValues();
|
||||
auto subname = subs.front().c_str();
|
||||
auto element = Data::ComplexGeoData::findElementName(subname);
|
||||
auto element = Data::findElementName(subname);
|
||||
if(!element || !element[0]) {
|
||||
mySubName = subs[0];
|
||||
if(hasSubElement)
|
||||
@@ -1499,7 +1499,7 @@ void LinkBaseExtension::parseSubName() const {
|
||||
mySubName = std::string(subname,element-subname);
|
||||
for(std::size_t i=1;i<subs.size();++i) {
|
||||
auto &sub = subs[i];
|
||||
element = Data::ComplexGeoData::findElementName(sub.c_str());
|
||||
element = Data::findElementName(sub.c_str());
|
||||
if(element && element[0] && boost::starts_with(sub,mySubName))
|
||||
mySubElements.emplace_back(element);
|
||||
}
|
||||
@@ -1938,7 +1938,7 @@ void LinkBaseExtension::onExtendedDocumentRestored() {
|
||||
} else {
|
||||
std::set<std::string> subset(mySubElements.begin(),mySubElements.end());
|
||||
auto sub = xlink->getSubValues().front();
|
||||
auto element = Data::ComplexGeoData::findElementName(sub.c_str());
|
||||
auto element = Data::findElementName(sub.c_str());
|
||||
if(element && element[0]) {
|
||||
subset.insert(element);
|
||||
sub.resize(element - sub.c_str());
|
||||
|
||||
@@ -26,19 +26,26 @@
|
||||
# include <unordered_set>
|
||||
#endif
|
||||
|
||||
//#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "MappedName.h"
|
||||
|
||||
using namespace Data;
|
||||
#include "Base/Console.h"
|
||||
|
||||
//#include <boost/functional/hash.hpp>
|
||||
#include <boost/iostreams/device/array.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
|
||||
|
||||
void MappedName::compact()
|
||||
FC_LOG_LEVEL_INIT("MappedName", true, 2);
|
||||
|
||||
namespace Data {
|
||||
|
||||
void MappedName::compact() const
|
||||
{
|
||||
auto self = const_cast<MappedName*>(this); //FIXME this is a workaround for a single call in ElementMap::addName()
|
||||
|
||||
if (this->raw) {
|
||||
this->data = QByteArray(this->data.constData(), this->data.size());
|
||||
this->raw = false;
|
||||
self->data = QByteArray(self->data.constData(), self->data.size());
|
||||
self->raw = false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -51,3 +58,158 @@ void MappedName::compact()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int MappedName::findTagInElementName(long* tag, int* len, const char* postfix,
|
||||
char* type, bool negative, bool recursive) const
|
||||
{
|
||||
bool hex = true;
|
||||
int pos = this->rfind(POSTFIX_TAG);
|
||||
|
||||
// Example name, tagPosfix == ;:H
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// ^
|
||||
// |
|
||||
// pos
|
||||
|
||||
if(pos < 0) {
|
||||
pos = this->rfind(POSTFIX_DECIMAL_TAG);
|
||||
if (pos < 0)
|
||||
return -1;
|
||||
hex = false;
|
||||
}
|
||||
int offset = pos + (int)POSTFIX_TAG_SIZE;
|
||||
long _tag = 0;
|
||||
int _len = 0;
|
||||
char sep = 0;
|
||||
char sep2 = 0;
|
||||
char tp = 0;
|
||||
char eof = 0;
|
||||
|
||||
int size;
|
||||
const char *s = this->toConstString(offset, size);
|
||||
|
||||
// check if the number followed by the tagPosfix is negative
|
||||
bool isNegative = (s[0] == '-');
|
||||
if (isNegative) {
|
||||
++s;
|
||||
--size;
|
||||
}
|
||||
boost::iostreams::stream<boost::iostreams::array_source> iss(s, size);
|
||||
if (!hex) {
|
||||
// no hex is an older version of the encoding scheme
|
||||
iss >> _tag >> sep;
|
||||
} else {
|
||||
// The purpose of tag postfix is to encode one model operation. The
|
||||
// 'tag' field is used to record the own object ID of that model shape,
|
||||
// and the 'len' field indicates the length of the operation codes
|
||||
// before the tag postfix. These fields are in hex. The trailing 'F' is
|
||||
// the shape type of this element, 'F' for face, 'E' edge, and 'V' vertex.
|
||||
//
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// | | ^^ ^^
|
||||
// | | | |
|
||||
// ---len = 0x10--- tag len
|
||||
|
||||
iss >> std::hex;
|
||||
// _tag field can be skipped, if it is 0
|
||||
if (s[0] == ',' || s[0] == ':')
|
||||
iss >> sep;
|
||||
else
|
||||
iss >> _tag >> sep;
|
||||
}
|
||||
|
||||
if (isNegative)
|
||||
_tag = -_tag;
|
||||
|
||||
if (sep == ':') {
|
||||
// ':' is followed by _len field.
|
||||
//
|
||||
// For decTagPostfix() (i.e. older encoding scheme), this is the length
|
||||
// of the string before the entire postfix (A postfix may contain
|
||||
// multiple segments usually separated by ELEMENT_MAP_PREFIX.
|
||||
//
|
||||
// For newer POSTFIX_TAG, this counts the number of characters that
|
||||
// proceeds this tag postfix segment that forms the op code (see
|
||||
// example above).
|
||||
//
|
||||
// The reason of this change is so that the postfix can stay the same
|
||||
// regardless of the prefix, which can increase memory efficiency.
|
||||
//
|
||||
iss >> _len >> sep2 >> tp >> eof;
|
||||
|
||||
// The next separator to look for is either ':' for older tag postfix, or ','
|
||||
if (!hex && sep2 == ':')
|
||||
sep2 = ',';
|
||||
}
|
||||
else if (hex && sep == ',') {
|
||||
// ',' is followed by a single character that indicates the element type.
|
||||
iss >> tp >> eof;
|
||||
sep = ':';
|
||||
sep2 = ',';
|
||||
}
|
||||
|
||||
if (_len < 0 || sep != ':' || sep2 != ',' || tp == 0 || eof != 0)
|
||||
return -1;
|
||||
|
||||
if (hex) {
|
||||
if (pos-_len < 0)
|
||||
return -1;
|
||||
if (_len && recursive && (tag || len)) {
|
||||
// in case of recursive tag postfix (used by hierarchy element
|
||||
// map), look for any embedded tag postifx
|
||||
int next = MappedName::fromRawData(*this, pos-_len, _len).rfind(POSTFIX_TAG);
|
||||
if (next >= 0) {
|
||||
next += pos - _len;
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// ^ ^
|
||||
// | |
|
||||
// next pos
|
||||
//
|
||||
// There maybe other operation codes after this embedded tag
|
||||
// postfix, search for the sperator.
|
||||
//
|
||||
int end;
|
||||
if (pos == next)
|
||||
end = -1;
|
||||
else
|
||||
end = MappedName::fromRawData(*this, next+1, pos-next-1).find(ELEMENT_MAP_PREFIX);
|
||||
if (end >= 0) {
|
||||
end += next+1;
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// ^
|
||||
// |
|
||||
// end
|
||||
_len = pos - end;
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// | |
|
||||
// -- len --
|
||||
} else
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Now convert the 'len' field back to the length of the remaining name
|
||||
//
|
||||
// #94;:G0;XTR;:H19:8,F;:H1a,F;BND:-1:0;:H1b:10,F
|
||||
// | |
|
||||
// ----------- len -----------
|
||||
_len = pos - _len;
|
||||
}
|
||||
if(type)
|
||||
*type = tp;
|
||||
if(tag) {
|
||||
if (_tag == 0 && recursive)
|
||||
return MappedName(*this, 0, _len).findTagInElementName(tag, len, postfix, type, negative);
|
||||
if(_tag>0 || negative)
|
||||
*tag = _tag;
|
||||
else
|
||||
*tag = -_tag;
|
||||
}
|
||||
if(len)
|
||||
*len = _len;
|
||||
if(postfix)
|
||||
this->toString(*postfix, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,15 +26,19 @@
|
||||
#define APP_MAPPED_NAME_H
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QVector>
|
||||
|
||||
#include "ComplexGeoData.h"
|
||||
#include "IndexedName.h"
|
||||
#include "StringHasher.h"
|
||||
#include "ElementNamingUtils.h"
|
||||
|
||||
|
||||
namespace Data
|
||||
@@ -62,8 +66,8 @@ public:
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
if (boost::starts_with(name, ComplexGeoData::elementMapPrefix())) {
|
||||
name += ComplexGeoData::elementMapPrefix().size();
|
||||
if (boost::starts_with(name, ELEMENT_MAP_PREFIX)) {
|
||||
name += ELEMENT_MAP_PREFIX_SIZE;
|
||||
}
|
||||
|
||||
data = size < 0 ? QByteArray(name) : QByteArray(name, size);
|
||||
@@ -78,9 +82,9 @@ public:
|
||||
{
|
||||
auto size = nameString.size();
|
||||
const char* name = nameString.c_str();
|
||||
if (boost::starts_with(nameString, ComplexGeoData::elementMapPrefix())) {
|
||||
name += ComplexGeoData::elementMapPrefix().size();
|
||||
size -= ComplexGeoData::elementMapPrefix().size();
|
||||
if (boost::starts_with(nameString, ELEMENT_MAP_PREFIX)) {
|
||||
name += ELEMENT_MAP_PREFIX_SIZE;
|
||||
size -= ELEMENT_MAP_PREFIX_SIZE;
|
||||
}
|
||||
data = QByteArray(name, static_cast<int>(size));
|
||||
}
|
||||
@@ -622,7 +626,7 @@ public:
|
||||
const char* appendToBufferWithPrefix(std::string& buf) const
|
||||
{
|
||||
if (!toIndexedName()) {
|
||||
buf += ComplexGeoData::elementMapPrefix();
|
||||
buf += ELEMENT_MAP_PREFIX;
|
||||
}
|
||||
appendToBuffer(buf);
|
||||
return buf.c_str();
|
||||
@@ -714,7 +718,7 @@ public:
|
||||
}
|
||||
|
||||
/// Ensure that this data is unshared, making a copy if necessary.
|
||||
void compact();
|
||||
void compact() const;
|
||||
|
||||
/// Boolean conversion is the inverse of empty(), returning true if there is data in either the
|
||||
/// data or postfix, and false if there is nothing in either.
|
||||
@@ -786,8 +790,7 @@ public:
|
||||
if (!searchTarget) {
|
||||
return -1;
|
||||
}
|
||||
if (startPosition < 0
|
||||
|| startPosition >= this->data.size()) {
|
||||
if (startPosition < 0 || startPosition >= this->data.size()) {
|
||||
if (startPosition >= data.size()) {
|
||||
startPosition -= data.size();
|
||||
}
|
||||
@@ -887,6 +890,22 @@ public:
|
||||
offset);
|
||||
}
|
||||
|
||||
/// Extract tag and other information from a encoded element name
|
||||
///
|
||||
/// \param tag: optional pointer to receive the extracted tag
|
||||
/// \param len: optional pointer to receive the length field after the tag field.
|
||||
/// This gives the length of the previous hashsed element name starting
|
||||
/// from the beginning of the give element name.
|
||||
/// \param postfix: optional pointer to receive the postfix starting at the found tag field.
|
||||
/// \param type: optional pointer to receive the element type character
|
||||
/// \param negative: return negative tag as it is. If disabled, then always return positive tag.
|
||||
/// Negative tag is sometimes used for element disambiguation.
|
||||
/// \param recursive: recursively find the last non-zero tag
|
||||
///
|
||||
/// \return Return the end position of the tag field, or return -1 if not found.
|
||||
int findTagInElementName(long* tag = 0, int* len = 0, const char* postfix = 0, char* type = 0,
|
||||
bool negative = false, bool recursive = true) const;
|
||||
|
||||
/// Get a hash for this MappedName
|
||||
std::size_t hash() const
|
||||
{
|
||||
@@ -899,6 +918,110 @@ private:
|
||||
bool raw;
|
||||
};
|
||||
|
||||
|
||||
typedef QVector<::App::StringIDRef> ElementIDRefs;
|
||||
|
||||
struct MappedNameRef
|
||||
{
|
||||
MappedName name;
|
||||
ElementIDRefs sids;
|
||||
std::unique_ptr<MappedNameRef> next;
|
||||
|
||||
MappedNameRef() = default;
|
||||
|
||||
MappedNameRef(const MappedName& name, const ElementIDRefs& sids = ElementIDRefs())
|
||||
: name(name),
|
||||
sids(sids)
|
||||
{
|
||||
compact();
|
||||
}
|
||||
|
||||
MappedNameRef(const MappedNameRef& other)
|
||||
: name(other.name),
|
||||
sids(other.sids)
|
||||
{}
|
||||
|
||||
MappedNameRef(MappedNameRef&& other)
|
||||
: name(std::move(other.name)),
|
||||
sids(std::move(other.sids)),
|
||||
next(std::move(other.next))
|
||||
{}
|
||||
|
||||
MappedNameRef& operator=(MappedNameRef&& other)
|
||||
{
|
||||
name = std::move(other.name);
|
||||
sids = std::move(other.sids);
|
||||
next = std::move(other.next);
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !name.empty();
|
||||
}
|
||||
|
||||
void append(const MappedName& name, const ElementIDRefs sids = ElementIDRefs())
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
if (!this->name) {
|
||||
this->name = name;
|
||||
this->sids = sids;
|
||||
compact();
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<MappedNameRef> n(new MappedNameRef(name, sids));
|
||||
if (!this->next)
|
||||
this->next = std::move(n);
|
||||
else {
|
||||
this->next.swap(n);
|
||||
this->next->next = std::move(n);
|
||||
}
|
||||
}
|
||||
|
||||
void compact()
|
||||
{
|
||||
if (sids.size() > 1) {
|
||||
std::sort(sids.begin(), sids.end());
|
||||
sids.erase(std::unique(sids.begin(), sids.end()), sids.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool erase(const MappedName& name)
|
||||
{
|
||||
if (this->name == name) {
|
||||
this->name.clear();
|
||||
this->sids.clear();
|
||||
if (this->next) {
|
||||
this->name = std::move(this->next->name);
|
||||
this->sids = std::move(this->next->sids);
|
||||
std::unique_ptr<MappedNameRef> tmp;
|
||||
tmp.swap(this->next);
|
||||
this->next = std::move(tmp->next);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (std::unique_ptr<MappedNameRef>* p = &this->next; *p; p = &(*p)->next) {
|
||||
if ((*p)->name == name) {
|
||||
std::unique_ptr<MappedNameRef> tmp;
|
||||
tmp.swap(*p);
|
||||
*p = std::move(tmp->next);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->name.clear();
|
||||
this->sids.clear();
|
||||
this->next.reset();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <zipios++/zipios-config.h>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#endif
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/Link.h>
|
||||
@@ -294,8 +294,8 @@ void StdCmdLinkMakeRelative::activated(int) {
|
||||
if(!sel.pObject || !sel.pObject->getNameInDocument())
|
||||
continue;
|
||||
auto key = std::make_pair(sel.pObject,
|
||||
Data::ComplexGeoData::noElementName(sel.SubName));
|
||||
auto element = Data::ComplexGeoData::findElementName(sel.SubName);
|
||||
Data::noElementName(sel.SubName));
|
||||
auto element = Data::findElementName(sel.SubName);
|
||||
auto &info = linkInfo[key];
|
||||
info.first = sel.pResolvedObject;
|
||||
if(element && element[0])
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
#endif
|
||||
|
||||
#include <App/AutoTransaction.h>
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <App/Transactions.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Matrix.h>
|
||||
@@ -408,7 +408,7 @@ bool Document::setEdit(Gui::ViewProvider* p, int ModNum, const char *subname)
|
||||
d->_editSubname.clear();
|
||||
|
||||
if (subname) {
|
||||
const char *element = Data::ComplexGeoData::findElementName(subname);
|
||||
const char *element = Data::findElementName(subname);
|
||||
if (element) {
|
||||
d->_editSubname = std::string(subname,element-subname);
|
||||
d->_editSubElement = element;
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
#endif
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include "SoFCUnifiedSelection.h"
|
||||
@@ -624,7 +624,7 @@ bool SoFCUnifiedSelection::setSelection(const std::vector<PickedInfo> &infos, bo
|
||||
objectName << ", " << subName);
|
||||
std::string newElement;
|
||||
if(subSelected) {
|
||||
newElement = Data::ComplexGeoData::newElementName(subSelected);
|
||||
newElement = Data::newElementName(subSelected);
|
||||
subSelected = newElement.c_str();
|
||||
std::string nextsub;
|
||||
const char *next = strrchr(subSelected,'.');
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
# include <sstream>
|
||||
#endif
|
||||
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <App/Document.h>
|
||||
|
||||
#include "TaskElementColors.h"
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
auto obj = vpParent->getObject();
|
||||
editDoc = obj->getDocument()->getName();
|
||||
editObj = obj->getNameInDocument();
|
||||
editSub = Data::ComplexGeoData::noElementName(editSub.c_str());
|
||||
editSub = Data::noElementName(editSub.c_str());
|
||||
}
|
||||
}
|
||||
if(editDoc.empty()) {
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
c.setRgbF(color.r,color.g,color.b,1.0-color.a);
|
||||
px.fill(c);
|
||||
auto item = new QListWidgetItem(QIcon(px),
|
||||
QString::fromLatin1(Data::ComplexGeoData::oldElementName(v.first.c_str()).c_str()),
|
||||
QString::fromLatin1(Data::oldElementName(v.first.c_str()).c_str()),
|
||||
ui->elementList);
|
||||
item->setData(Qt::UserRole,c);
|
||||
item->setData(Qt::UserRole+1,QString::fromLatin1(v.first.c_str()));
|
||||
@@ -419,7 +419,7 @@ void ElementColors::onHideSelectionClicked() {
|
||||
if(!subs.empty()) {
|
||||
for(auto &sub : subs) {
|
||||
if(boost::starts_with(sub,d->editSub)) {
|
||||
auto name = Data::ComplexGeoData::noElementName(sub.c_str()+d->editSub.size());
|
||||
auto name = Data::noElementName(sub.c_str()+d->editSub.size());
|
||||
name += ViewProvider::hiddenMarker();
|
||||
d->addItem(-1,name.c_str());
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#endif
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <App/ComplexGeoData.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <App/Document.h>
|
||||
#include <Base/BoundBoxPy.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
@@ -635,7 +635,7 @@ public:
|
||||
break;
|
||||
}
|
||||
// new style mapped sub-element
|
||||
if(Data::ComplexGeoData::isMappedElement(dot+1))
|
||||
if(Data::isMappedElement(dot+1))
|
||||
break;
|
||||
auto next = strchr(dot+1,'.');
|
||||
if(!next) {
|
||||
@@ -1031,7 +1031,7 @@ void LinkView::setLinkViewObject(ViewProviderDocumentObject *vpd,
|
||||
subInfo.clear();
|
||||
for(const auto &sub : subs) {
|
||||
if(sub.empty()) continue;
|
||||
const char *subelement = Data::ComplexGeoData::findElementName(sub.c_str());
|
||||
const char *subelement = Data::findElementName(sub.c_str());
|
||||
std::string subname = sub.substr(0,subelement-sub.c_str());
|
||||
auto it = subInfo.find(subname);
|
||||
if(it == subInfo.end()) {
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/FileInfo.h>
|
||||
@@ -102,7 +103,6 @@
|
||||
#include "TopoShapeSolidPy.h"
|
||||
#include "TopoShapeWirePy.h"
|
||||
|
||||
|
||||
#ifdef FCUseFreeType
|
||||
# include "FT2FC.h"
|
||||
#endif
|
||||
@@ -2286,14 +2286,14 @@ private:
|
||||
const char *subname;
|
||||
if (!PyArg_ParseTuple(args.ptr(), "s",&subname))
|
||||
throw Py::Exception();
|
||||
auto element = Data::ComplexGeoData::findElementName(subname);
|
||||
auto element = Data::findElementName(subname);
|
||||
std::string sub(subname,element-subname);
|
||||
Py::List list;
|
||||
list.append(Py::String(sub));
|
||||
const char *dot = strchr(element,'.');
|
||||
if(!dot)
|
||||
dot = element+strlen(element);
|
||||
const char *mapped = Data::ComplexGeoData::isMappedElement(element);
|
||||
const char *mapped = Data::isMappedElement(element);
|
||||
if(mapped)
|
||||
list.append(Py::String(std::string(mapped,dot-mapped)));
|
||||
else
|
||||
@@ -2317,8 +2317,8 @@ private:
|
||||
if (!subname.empty() && subname[subname.size()-1]!='.')
|
||||
subname += '.';
|
||||
if (mapped && mapped[0]) {
|
||||
if (!Data::ComplexGeoData::isMappedElement(mapped))
|
||||
subname += Data::ComplexGeoData::elementMapPrefix();
|
||||
if (!Data::isMappedElement(mapped))
|
||||
subname += Data::ELEMENT_MAP_PREFIX;
|
||||
subname += mapped;
|
||||
}
|
||||
if (element && element[0]) {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include <App/FeaturePythonPyImp.h>
|
||||
#include <App/Link.h>
|
||||
#include <App/GeoFeatureGroupExtension.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Placement.h>
|
||||
#include <Base/Rotation.h>
|
||||
@@ -65,7 +66,6 @@
|
||||
#include "PartPyCXX.h"
|
||||
#include "TopoShapePy.h"
|
||||
|
||||
|
||||
using namespace Part;
|
||||
namespace bp = boost::placeholders;
|
||||
|
||||
@@ -121,7 +121,7 @@ App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
{
|
||||
// having '.' inside subname means it is referencing some children object,
|
||||
// instead of any sub-element from ourself
|
||||
if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.'))
|
||||
if(subname && !Data::isMappedElement(subname) && strchr(subname,'.'))
|
||||
return App::DocumentObject::getSubObject(subname,pyObj,pmat,transform,depth);
|
||||
|
||||
Base::Matrix4D _mat;
|
||||
@@ -280,7 +280,7 @@ static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subna
|
||||
if(powner) *powner = nullptr;
|
||||
|
||||
std::string _subname;
|
||||
auto subelement = Data::ComplexGeoData::findElementName(subname);
|
||||
auto subelement = Data::findElementName(subname);
|
||||
if(!needSubElement && subname) {
|
||||
// strip out element name if not needed
|
||||
if(subelement && *subelement) {
|
||||
@@ -413,7 +413,7 @@ static TopoShape _getTopoShape(const App::DocumentObject *obj, const char *subna
|
||||
continue;
|
||||
}else{
|
||||
if(link && !link->getShowElementValue())
|
||||
shape = baseShape.makeTransform(mat,(TopoShape::indexPostfix()+childName).c_str());
|
||||
shape = baseShape.makeTransform(mat,(Data::POSTFIX_INDEX + childName).c_str());
|
||||
else {
|
||||
shape = baseShape.makeTransform(mat);
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
#endif // _PreComp_
|
||||
|
||||
#include <App/Material.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Base/BoundBox.h>
|
||||
#include <Base/Builder3D.h>
|
||||
#include <Base/Console.h>
|
||||
@@ -472,7 +473,7 @@ TopAbs_ShapeEnum TopoShape::shapeType(const char *type, bool silent) {
|
||||
}
|
||||
}
|
||||
if(!silent) {
|
||||
if(Data::ComplexGeoData::hasMissingElement(type))
|
||||
if(Data::hasMissingElement(type))
|
||||
FC_THROWM(Base::CADKernelError,"missing shape element: " << (type?type:"?"));
|
||||
FC_THROWM(Base::CADKernelError,"invalid shape type: " << (type?type:"?"));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "App/DocumentObject.h"
|
||||
#include <App/FeaturePythonPyImp.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include "App/OriginFeature.h"
|
||||
#include <Base/Console.h>
|
||||
|
||||
@@ -237,7 +238,7 @@ Body* Feature::getFeatureBody() const {
|
||||
App::DocumentObject *Feature::getSubObject(const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
|
||||
{
|
||||
if (subname && subname != Data::ComplexGeoData::findElementName(subname)) {
|
||||
if (subname && subname != Data::findElementName(subname)) {
|
||||
const char * dot = strchr(subname,'.');
|
||||
if (dot) {
|
||||
auto body = PartDesign::Body::findBodyOf(this);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <App/GroupExtension.h>
|
||||
#include <App/Link.h>
|
||||
#include <App/OriginFeature.h>
|
||||
#include <App/ElementNamingUtils.h>
|
||||
#include <Mod/Part/App/TopoShape.h>
|
||||
|
||||
#include "ShapeBinder.h"
|
||||
@@ -382,7 +383,7 @@ App::DocumentObject* SubShapeBinder::getSubObject(const char* subname, PyObject*
|
||||
auto sobj = Part::Feature::getSubObject(subname, pyObj, mat, transform, depth);
|
||||
if (sobj)
|
||||
return sobj;
|
||||
if (Data::ComplexGeoData::findElementName(subname) == subname)
|
||||
if (Data::findElementName(subname) == subname)
|
||||
return nullptr;
|
||||
|
||||
const char* dot = strchr(subname, '.');
|
||||
@@ -405,7 +406,7 @@ App::DocumentObject* SubShapeBinder::getSubObject(const char* subname, PyObject*
|
||||
}
|
||||
else if (!boost::equals(sobj->getNameInDocument(), name))
|
||||
continue;
|
||||
name = Data::ComplexGeoData::noElementName(sub.c_str());
|
||||
name = Data::noElementName(sub.c_str());
|
||||
name += dot + 1;
|
||||
if (mat && transform)
|
||||
*mat *= Placement.getValue().toMatrix();
|
||||
@@ -640,7 +641,7 @@ void SubShapeBinder::update(SubShapeBinder::UpdateOption options) {
|
||||
std::ostringstream ss;
|
||||
ss << "Failed to obtain shape " <<
|
||||
obj->getFullName() << '.'
|
||||
<< Data::ComplexGeoData::oldElementName(sub.c_str());
|
||||
<< Data::oldElementName(sub.c_str());
|
||||
errMsg = ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,537 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "App/ElementMap.h"
|
||||
#include <App/Application.h>
|
||||
#include <App/ElementMap.h>
|
||||
|
||||
#include <sstream>
|
||||
// NOLINTBEGIN(readability-magic-numbers)
|
||||
|
||||
|
||||
// this is a "holder" class used for simpler testing of ElementMap in the context of a class
|
||||
class LessComplexPart
|
||||
{
|
||||
public:
|
||||
LessComplexPart(long tag, const std::string& nameStr, App::StringHasherRef hasher)
|
||||
: elementMapPtr(std::make_shared<Data::ElementMap>())
|
||||
, Tag(tag)
|
||||
, name(nameStr)
|
||||
{
|
||||
// object also have Vertexes etc and the face count varies; but that is not important
|
||||
// here since we are not testing a real model
|
||||
// the "MappedName" is left blank for now
|
||||
Data::IndexedName face1("Face", 1);
|
||||
Data::IndexedName face2("Face", 2);
|
||||
Data::IndexedName face3("Face", 3);
|
||||
Data::IndexedName face4("Face", 4);
|
||||
Data::IndexedName face5("Face", 5);
|
||||
Data::IndexedName face6("Face", 6);
|
||||
elementMapPtr->hasher = hasher;
|
||||
elementMapPtr->setElementName(face1, Data::MappedName(face1), Tag);
|
||||
elementMapPtr->setElementName(face2, Data::MappedName(face2), Tag);
|
||||
elementMapPtr->setElementName(face3, Data::MappedName(face3), Tag);
|
||||
elementMapPtr->setElementName(face4, Data::MappedName(face4), Tag);
|
||||
elementMapPtr->setElementName(face5, Data::MappedName(face5), Tag);
|
||||
elementMapPtr->setElementName(face6, Data::MappedName(face6), Tag);
|
||||
}
|
||||
|
||||
Data::ElementMapPtr elementMapPtr;
|
||||
mutable long Tag;
|
||||
Data::MappedName name;
|
||||
};
|
||||
|
||||
class ElementMapTest: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
int argc = 1;
|
||||
char* argv[] = {"FreeCAD"};
|
||||
App::Application::Config()["ExeName"] = "FreeCAD";
|
||||
App::Application::init(argc, argv);
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
App::GetApplication().newDocument("test", "testUser");
|
||||
_sids = &_sid;
|
||||
_hasher = Base::Reference<App::StringHasher>(new App::StringHasher);
|
||||
}
|
||||
|
||||
// void TearDown() override {}
|
||||
|
||||
Data::ElementIDRefs _sid;
|
||||
QVector<App::StringIDRef>* _sids;
|
||||
App::StringHasherRef _hasher;
|
||||
};
|
||||
|
||||
TEST_F(ElementMapTest, defaultConstruction)
|
||||
{
|
||||
// Act
|
||||
Data::ElementMap elementMap = Data::ElementMap();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(elementMap.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, setElementNameDefaults)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap elementMap;
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
|
||||
// Act
|
||||
auto resultName = elementMap.setElementName(element, mappedName, 0);
|
||||
auto mappedToElement = elementMap.find(element);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(resultName, mappedName);
|
||||
EXPECT_EQ(mappedToElement, mappedName);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, setElementNameNoOverwrite)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap elementMap;
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
|
||||
// Act
|
||||
auto resultName = elementMap.setElementName(element, mappedName, 0);
|
||||
auto resultName2 = elementMap.setElementName(element, anotherMappedName, 0, _sids, false);
|
||||
auto mappedToElement = elementMap.find(element);
|
||||
auto findAllResult = elementMap.findAll(element);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(resultName, mappedName);
|
||||
EXPECT_EQ(resultName2, anotherMappedName);
|
||||
EXPECT_EQ(mappedToElement, mappedName);
|
||||
EXPECT_EQ(findAllResult.size(), 2);
|
||||
EXPECT_EQ(findAllResult[0].first, mappedName);
|
||||
EXPECT_EQ(findAllResult[1].first, anotherMappedName);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, setElementNameWithOverwrite)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap elementMap;
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
|
||||
// Act
|
||||
auto resultName = elementMap.setElementName(element, mappedName, 0);
|
||||
auto resultName2 = elementMap.setElementName(element, anotherMappedName, 0, _sids, true);
|
||||
auto mappedToElement = elementMap.find(element);
|
||||
auto findAllResult = elementMap.findAll(element);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(resultName, mappedName);
|
||||
EXPECT_EQ(resultName2, anotherMappedName);
|
||||
EXPECT_EQ(mappedToElement, anotherMappedName);
|
||||
EXPECT_EQ(findAllResult.size(), 1);
|
||||
EXPECT_EQ(findAllResult[0].first, anotherMappedName);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, setElementNameWithHashing)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap elementMap;
|
||||
std::ostringstream ss;
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName elementNameHolder(element);// Will get modified by the encoder
|
||||
const Data::MappedName expectedName(element);
|
||||
|
||||
// Act
|
||||
elementMap.encodeElementName(
|
||||
element.getType()[0], elementNameHolder, ss, nullptr, 0, nullptr, 0);
|
||||
auto resultName = elementMap.setElementName(element, elementNameHolder, 0, _sids);
|
||||
auto mappedToElement = elementMap.find(element);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(resultName, expectedName);
|
||||
EXPECT_EQ(mappedToElement, expectedName);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, eraseMappedName)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap elementMap;
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
elementMap.setElementName(element, mappedName, 0);
|
||||
elementMap.setElementName(element, anotherMappedName, 0);
|
||||
|
||||
// Act
|
||||
auto sizeBefore = elementMap.size();
|
||||
auto findAllBefore = elementMap.findAll(element);
|
||||
|
||||
elementMap.erase(anotherMappedName);
|
||||
auto sizeAfter = elementMap.size();
|
||||
auto findAllAfter = elementMap.findAll(element);
|
||||
|
||||
elementMap.erase(anotherMappedName);
|
||||
auto sizeAfterRepeat = elementMap.size();
|
||||
auto findAllAfterRepeat = elementMap.findAll(element);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(sizeBefore, 2);
|
||||
EXPECT_EQ(findAllBefore.size(), 2);
|
||||
EXPECT_EQ(findAllBefore[0].first, mappedName);
|
||||
EXPECT_EQ(findAllBefore[1].first, anotherMappedName);
|
||||
|
||||
EXPECT_EQ(sizeAfter, 1);
|
||||
EXPECT_EQ(findAllAfter.size(), 1);
|
||||
EXPECT_EQ(findAllAfter[0].first, mappedName);
|
||||
|
||||
EXPECT_EQ(sizeAfterRepeat, 1);
|
||||
EXPECT_EQ(findAllAfterRepeat.size(), 1);
|
||||
EXPECT_EQ(findAllAfterRepeat[0].first, mappedName);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, eraseIndexedName)
|
||||
{
|
||||
// Arrange
|
||||
// Create two elements, edge1 and edge2, that have two mapped names each.
|
||||
Data::ElementMap elementMap;
|
||||
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
elementMap.setElementName(element, mappedName, 0);
|
||||
elementMap.setElementName(element, anotherMappedName, 0);
|
||||
|
||||
Data::IndexedName element2("Edge", 2);
|
||||
Data::MappedName mappedName2("TEST2");
|
||||
Data::MappedName anotherMappedName2("ANOTHERTEST2");
|
||||
elementMap.setElementName(element2, mappedName2, 0);
|
||||
elementMap.setElementName(element2, anotherMappedName2, 0);
|
||||
|
||||
// Act
|
||||
auto sizeBefore = elementMap.size();
|
||||
auto findAllBefore = elementMap.findAll(element2);
|
||||
|
||||
elementMap.erase(element2);
|
||||
auto sizeAfter = elementMap.size();
|
||||
auto findAllAfter = elementMap.findAll(element2);
|
||||
|
||||
elementMap.erase(element2);
|
||||
auto sizeAfterRepeat = elementMap.size();
|
||||
auto findAllAfterRepeat = elementMap.findAll(element2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(sizeBefore, 4);
|
||||
EXPECT_EQ(findAllBefore.size(), 2);
|
||||
EXPECT_EQ(findAllBefore[0].first, mappedName2);
|
||||
EXPECT_EQ(findAllBefore[1].first, anotherMappedName2);
|
||||
|
||||
EXPECT_EQ(sizeAfter, 2);
|
||||
EXPECT_EQ(findAllAfter.size(), 0);
|
||||
|
||||
EXPECT_EQ(sizeAfterRepeat, 2);
|
||||
EXPECT_EQ(findAllAfterRepeat.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, findMappedName)
|
||||
{
|
||||
// Arrange
|
||||
// Create two elements, edge1 and edge2, that have two mapped names each.
|
||||
Data::ElementMap elementMap;
|
||||
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
elementMap.setElementName(element, mappedName, 0);
|
||||
elementMap.setElementName(element, anotherMappedName, 0);
|
||||
|
||||
Data::IndexedName element2("Edge", 2);
|
||||
Data::MappedName mappedName2("TEST2");
|
||||
Data::MappedName anotherMappedName2("ANOTHERTEST2");
|
||||
elementMap.setElementName(element2, mappedName2, 0);
|
||||
elementMap.setElementName(element2, anotherMappedName2, 0);
|
||||
|
||||
// Act
|
||||
auto findResult = elementMap.find(mappedName);
|
||||
auto findResult2 = elementMap.find(mappedName2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(findResult, element);
|
||||
EXPECT_EQ(findResult2, element2);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, findIndexedName)
|
||||
{
|
||||
// Arrange
|
||||
// Create two elements, edge1 and edge2, that have two mapped names each.
|
||||
Data::ElementMap elementMap;
|
||||
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
elementMap.setElementName(element, mappedName, 0);
|
||||
elementMap.setElementName(element, anotherMappedName, 0);
|
||||
|
||||
Data::IndexedName element2("Edge", 2);
|
||||
Data::MappedName mappedName2("TEST2");
|
||||
Data::MappedName anotherMappedName2("ANOTHERTEST2");
|
||||
elementMap.setElementName(element2, mappedName2, 0);
|
||||
elementMap.setElementName(element2, anotherMappedName2, 0);
|
||||
|
||||
// Act
|
||||
// they return the first mapped name
|
||||
auto findResult = elementMap.find(element);
|
||||
auto findResult2 = elementMap.find(element2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(findResult, mappedName);
|
||||
EXPECT_EQ(findResult2, mappedName2);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, findAll)
|
||||
{
|
||||
// Arrange
|
||||
// Create two elements, edge1 and edge2, that have two mapped names each.
|
||||
Data::ElementMap elementMap;
|
||||
|
||||
Data::IndexedName element("Edge", 1);
|
||||
Data::MappedName mappedName("TEST");
|
||||
Data::MappedName anotherMappedName("ANOTHERTEST");
|
||||
elementMap.setElementName(element, mappedName, 0);
|
||||
elementMap.setElementName(element, anotherMappedName, 0);
|
||||
|
||||
Data::IndexedName element2("Edge", 2);
|
||||
Data::MappedName mappedName2("TEST2");
|
||||
Data::MappedName anotherMappedName2("ANOTHERTEST2");
|
||||
elementMap.setElementName(element2, mappedName2, 0);
|
||||
elementMap.setElementName(element2, anotherMappedName2, 0);
|
||||
|
||||
// Act
|
||||
// they return the first mapped name
|
||||
auto findResult = elementMap.findAll(element);
|
||||
auto findResult2 = elementMap.findAll(element2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(findResult.size(), 2);
|
||||
EXPECT_EQ(findResult[0].first, mappedName);
|
||||
EXPECT_EQ(findResult[1].first, anotherMappedName);
|
||||
EXPECT_EQ(findResult2.size(), 2);
|
||||
EXPECT_EQ(findResult2[0].first, mappedName2);
|
||||
EXPECT_EQ(findResult2[1].first, anotherMappedName2);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, mimicOnePart)
|
||||
{
|
||||
// Arrange
|
||||
// pattern: new doc, create Cube
|
||||
// for a single part, there is no "naming algo" to speak of
|
||||
std::ostringstream ss;
|
||||
auto docName = "Unnamed";
|
||||
LessComplexPart cube(1L, "Box", _hasher);
|
||||
|
||||
// Act
|
||||
auto children = cube.elementMapPtr->getAll();
|
||||
ss << docName << "#" << cube.name << "."
|
||||
<< cube.elementMapPtr->find(Data::IndexedName("Face", 6));
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(children.size(), 6);
|
||||
EXPECT_EQ(children[0].index.toString(), "Face1");
|
||||
EXPECT_EQ(children[0].name.toString(), "Face1");
|
||||
EXPECT_EQ(children[1].index.toString(), "Face2");
|
||||
EXPECT_EQ(children[1].name.toString(), "Face2");
|
||||
EXPECT_EQ(children[2].index.toString(), "Face3");
|
||||
EXPECT_EQ(children[2].name.toString(), "Face3");
|
||||
EXPECT_EQ(children[3].index.toString(), "Face4");
|
||||
EXPECT_EQ(children[3].name.toString(), "Face4");
|
||||
EXPECT_EQ(children[4].index.toString(), "Face5");
|
||||
EXPECT_EQ(children[4].name.toString(), "Face5");
|
||||
EXPECT_EQ(children[5].index.toString(), "Face6");
|
||||
EXPECT_EQ(children[5].name.toString(), "Face6");
|
||||
EXPECT_EQ(ss.str(), "Unnamed#Box.Face6");
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, mimicSimpleUnion)
|
||||
{
|
||||
// Arrange
|
||||
// pattern: new doc, create Cube, create Cylinder, Union of both (Cube first)
|
||||
std::ostringstream ss;
|
||||
std::ostringstream finalSs;
|
||||
char* docName = "Unnamed";
|
||||
|
||||
LessComplexPart cube(1L, "Box", _hasher);
|
||||
LessComplexPart cylinder(2L, "Cylinder", _hasher);
|
||||
// Union (Fusion) operation via the Part Workbench
|
||||
LessComplexPart unionPart(3L, "Fusion", _hasher);
|
||||
|
||||
// we are only going to simulate one face for testing purpose
|
||||
Data::IndexedName uface3("Face", 3);
|
||||
auto PartOp = "FUS";// Part::OpCodes::Fuse;
|
||||
|
||||
// Act
|
||||
// act: simulate a union/fuse operation
|
||||
auto parent = cube.elementMapPtr->getAll()[5];
|
||||
Data::MappedName postfixHolder(std::string(Data::POSTFIX_MOD) + "2");
|
||||
unionPart.elementMapPtr->encodeElementName(
|
||||
postfixHolder[0], postfixHolder, ss, nullptr, unionPart.Tag, nullptr, unionPart.Tag);
|
||||
auto postfixStr = postfixHolder.toString() + Data::ELEMENT_MAP_PREFIX + PartOp;
|
||||
|
||||
// act: with the fuse op, name against the cube's Face6
|
||||
Data::MappedName uface3Holder(parent.index);
|
||||
// we will invoke the encoder for face 3
|
||||
unionPart.elementMapPtr->encodeElementName(
|
||||
uface3Holder[0], uface3Holder, ss, nullptr, unionPart.Tag, postfixStr.c_str(), cube.Tag);
|
||||
unionPart.elementMapPtr->setElementName(uface3, uface3Holder, unionPart.Tag, nullptr, true);
|
||||
|
||||
// act: generate a full toponame string for testing purposes
|
||||
finalSs << docName << "#" << unionPart.name;
|
||||
finalSs << ".";
|
||||
finalSs << Data::ELEMENT_MAP_PREFIX + unionPart.elementMapPtr->find(uface3).toString();
|
||||
finalSs << ".";
|
||||
finalSs << uface3;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(postfixStr, ":M2;FUS");
|
||||
EXPECT_EQ(unionPart.elementMapPtr->find(uface3).toString(), "Face6;:M2;FUS;:H1:8,F");
|
||||
EXPECT_EQ(finalSs.str(), "Unnamed#Fusion.;Face6;:M2;FUS;:H1:8,F.Face3");
|
||||
|
||||
// explanation of "Fusion.;Face6;:M2;FUS;:H2:3,F.Face3" toponame
|
||||
// Note: every postfix is prefixed by semicolon
|
||||
// Note: the start/middle/end are separated by periods
|
||||
//
|
||||
// "Fusion" means that we are on the "Fusion" object.
|
||||
// "." we are done with the first part
|
||||
// ";Face6" means default inheritance comes from face 6 of the parent (which is a cube)
|
||||
// ";:M2" means that a Workbench op has happened. "M" is the "Mod" directory in the source tree?
|
||||
// ";FUS" means that a Fusion operation has happened. Notice the lack of a colon.
|
||||
// ";:H2" means the subtending object (cylinder) has a tag of 2
|
||||
// ":3" means the writing position is 3; literally how far into the current postfix we are
|
||||
// ",F" means are of type "F" which is short for "Face" of Face3 of Fusion.
|
||||
// "." we are done with the second part
|
||||
// "Face3" is the localized name
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, mimicOperationAgainstSelf)
|
||||
{
|
||||
// Arrange
|
||||
// pattern: new doc, create Cube, Mystery Op with self as target
|
||||
std::ostringstream ss;
|
||||
LessComplexPart finalPart(99L, "MysteryOp", _hasher);
|
||||
// we are only going to simulate one face for testing purpose
|
||||
Data::IndexedName uface3("Face", 3);
|
||||
auto PartOp = "MYS";
|
||||
auto ownFace6 = finalPart.elementMapPtr->getAll()[5];
|
||||
Data::MappedName uface3Holder(ownFace6.index);
|
||||
auto workbenchId = std::string(Data::POSTFIX_MOD) + "9999";
|
||||
|
||||
// Act
|
||||
// act: with the mystery op, name against its own Face6 for some reason
|
||||
Data::MappedName postfixHolder(workbenchId);
|
||||
finalPart.elementMapPtr->encodeElementName(
|
||||
postfixHolder[0], postfixHolder, ss, nullptr, finalPart.Tag, nullptr, finalPart.Tag);
|
||||
auto postfixStr = postfixHolder.toString() + Data::ELEMENT_MAP_PREFIX + PartOp;
|
||||
// we will invoke the encoder for face 3
|
||||
finalPart.elementMapPtr->encodeElementName(uface3Holder[0],
|
||||
uface3Holder,
|
||||
ss,
|
||||
nullptr,
|
||||
finalPart.Tag,
|
||||
postfixStr.c_str(),
|
||||
finalPart.Tag);
|
||||
// override not forced
|
||||
finalPart.elementMapPtr->setElementName(uface3, uface3Holder, finalPart.Tag, nullptr, false);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(postfixStr, ":M9999;MYS");
|
||||
EXPECT_EQ(finalPart.elementMapPtr->find(uface3).toString(), "Face3");// override not forced
|
||||
EXPECT_EQ(uface3Holder.toString(), "Face6;:M9999;MYS;:H63:b,F");
|
||||
// explaining ";Face6;:M2;MYS;:H2:3,F" name:
|
||||
//
|
||||
// ";Face6" means default inheritance comes from face 6 of the ownFace6 (which is itself)
|
||||
// ";:M9999" means that a Workbench op happened. "M" is the "Mod" directory in the source tree?
|
||||
// ";MYS" means that a "Mystery" operation has happened. Notice the lack of a colon.
|
||||
// ";:H63" means the subtending object (cylinder) has a tag of 99 (63 in hex)
|
||||
// ":b" means the writing position is b (hex); literally how far into the current postfix we are
|
||||
// ",F" means are of type "F" which is short for "Face" of Face3 of Fusion.
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, hasChildElementMapTest)
|
||||
{
|
||||
// Arrange
|
||||
Data::ElementMap::MappedChildElements child = {
|
||||
Data::IndexedName("face", 1), 2, 7, 4L, Data::ElementMapPtr(), QByteArray(""), _sid};
|
||||
std::vector<Data::ElementMap::MappedChildElements> children = {child};
|
||||
LessComplexPart cubeFull(3L, "FullBox", _hasher);
|
||||
cubeFull.elementMapPtr->addChildElements(cubeFull.Tag, children);
|
||||
//
|
||||
LessComplexPart cubeWithoutChildren(2L, "EmptyBox", _hasher);
|
||||
|
||||
// Act
|
||||
bool resultFull = cubeFull.elementMapPtr->hasChildElementMap();
|
||||
bool resultWhenEmpty = cubeWithoutChildren.elementMapPtr->hasChildElementMap();
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(resultFull);
|
||||
EXPECT_FALSE(resultWhenEmpty);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, hashChildMapsTest)
|
||||
{
|
||||
// Arrange
|
||||
LessComplexPart cube(1L, "Box", _hasher);
|
||||
auto childOneName = Data::IndexedName("Ping", 1);
|
||||
Data::ElementMap::MappedChildElements childOne = {
|
||||
childOneName,
|
||||
2,
|
||||
7,
|
||||
3L,
|
||||
Data::ElementMapPtr(),
|
||||
QByteArray("abcdefghij"),// postfix must be 10 or more bytes to invoke hasher
|
||||
_sid};
|
||||
std::vector<Data::ElementMap::MappedChildElements> children = {childOne};
|
||||
cube.elementMapPtr->addChildElements(cube.Tag, children);
|
||||
auto before = _hasher->getIDMap();
|
||||
|
||||
// Act
|
||||
cube.elementMapPtr->hashChildMaps(cube.Tag);
|
||||
|
||||
// Assert
|
||||
auto after = _hasher->getIDMap();
|
||||
EXPECT_EQ(before.size(), 0);
|
||||
EXPECT_EQ(after.size(), 1);
|
||||
}
|
||||
|
||||
TEST_F(ElementMapTest, addAndGetChildElementsTest)
|
||||
{
|
||||
// Arrange
|
||||
LessComplexPart cube(1L, "Box", _hasher);
|
||||
Data::ElementMap::MappedChildElements childOne = {
|
||||
Data::IndexedName("Ping", 1),
|
||||
2,
|
||||
7,
|
||||
3L,
|
||||
Data::ElementMapPtr(),
|
||||
QByteArray("abcdefghij"),// postfix must be 10 or more bytes to invoke hasher
|
||||
_sid};
|
||||
Data::ElementMap::MappedChildElements childTwo = {
|
||||
Data::IndexedName("Pong", 2), 2, 7, 4L, Data::ElementMapPtr(), QByteArray("abc"), _sid};
|
||||
std::vector<Data::ElementMap::MappedChildElements> children = {childOne, childTwo};
|
||||
|
||||
// Act
|
||||
cube.elementMapPtr->addChildElements(cube.Tag, children);
|
||||
auto result = cube.elementMapPtr->getChildElements();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(result.size(), 2);
|
||||
EXPECT_TRUE(
|
||||
std::any_of(result.begin(), result.end(), [](Data::ElementMap::MappedChildElements e) {
|
||||
return e.indexedName.toString() == "Ping1";
|
||||
}));
|
||||
EXPECT_TRUE(
|
||||
std::any_of(result.begin(), result.end(), [](Data::ElementMap::MappedChildElements e) {
|
||||
return e.indexedName.toString() == "Pong2";
|
||||
}));
|
||||
}
|
||||
|
||||
// NOLINTEND(readability-magic-numbers)
|
||||
|
||||
@@ -51,7 +51,7 @@ TEST(MappedName, namedConstructionWithMaxSize)
|
||||
TEST(MappedName, namedConstructionDiscardPrefix)
|
||||
{
|
||||
// Arrange
|
||||
std::string name = Data::ComplexGeoData::elementMapPrefix() + "TEST";
|
||||
std::string name = std::string(Data::ELEMENT_MAP_PREFIX) + "TEST";
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(name.c_str());
|
||||
@@ -80,7 +80,7 @@ TEST(MappedName, stringNamedConstruction)
|
||||
TEST(MappedName, stringNamedConstructionDiscardPrefix)
|
||||
{
|
||||
// Arrange
|
||||
std::string name = Data::ComplexGeoData::elementMapPrefix() + "TEST";
|
||||
std::string name = std::string(Data::ELEMENT_MAP_PREFIX) + "TEST";
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(name);
|
||||
@@ -560,7 +560,7 @@ TEST(MappedName, appendToBufferWithPrefix)
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::string buffer("STUFF");
|
||||
std::string elemMapPrefix = Data::ComplexGeoData::elementMapPrefix();
|
||||
std::string elemMapPrefix = Data::ELEMENT_MAP_PREFIX;
|
||||
|
||||
// Act
|
||||
mappedName.appendToBufferWithPrefix(buffer);
|
||||
@@ -586,7 +586,7 @@ TEST(MappedName, toPrefixedString)
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::string buffer("STUFF");
|
||||
std::string elemMapPrefix = Data::ComplexGeoData::elementMapPrefix();
|
||||
std::string elemMapPrefix = Data::ELEMENT_MAP_PREFIX;
|
||||
|
||||
// Act
|
||||
buffer += mappedName.toPrefixedString();
|
||||
|
||||
Reference in New Issue
Block a user