331 lines
13 KiB
C++
331 lines
13 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
/****************************************************************************
|
|
* Copyright (c) 2018-2022 Zheng, Lei (realthunder) *
|
|
* <realthunder.dev@gmail.com> *
|
|
* Copyright (c) 2023 FreeCAD Project Association *
|
|
* *
|
|
* 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 DATA_ELEMENTMAP_H
|
|
#define DATA_ELEMENTMAP_H
|
|
|
|
#include "FCGlobal.h"
|
|
|
|
#include "Application.h"
|
|
#include "MappedElement.h"
|
|
#include "StringHasher.h"
|
|
|
|
#include <cstring>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
|
|
namespace Data
|
|
{
|
|
|
|
class ElementMap;
|
|
using ElementMapPtr = std::shared_ptr<ElementMap>;
|
|
|
|
/** Element trace callback
|
|
*
|
|
* The callback has the following call signature
|
|
* (const std::string &name, size_t offset, long encodedTag, long tag) -> bool
|
|
*
|
|
* @param name: the current element name.
|
|
* @param offset: the offset skipping the encoded element name for the next iteration.
|
|
* @param encodedTag: the tag encoded inside the current element, which is usually the tag
|
|
* of the previous step in the shape history.
|
|
* @param tag: the tag of the current shape element.
|
|
*
|
|
* @sa traceElement()
|
|
*/
|
|
typedef std::function<bool(const MappedName&, int, long, long)> TraceCallback;
|
|
|
|
/* 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 hasherRef passed
|
|
* as a parameter. Finally do this recursively for all childEelementMaps as well.
|
|
*
|
|
* @param hasherRef 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& hasherRef) 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 stream: serialized stream
|
|
*/
|
|
void save(std::ostream& stream) 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 hasherRef: where all the StringIDs are stored
|
|
* @param stream: stream to deserialize
|
|
*/
|
|
ElementMapPtr restore(::App::StringHasherRef hasherRef, std::istream& stream);
|
|
|
|
|
|
/** 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 = nullptr,
|
|
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 ElementNamingUtils.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;
|
|
|
|
long getElementHistory(const MappedName& name,
|
|
long masterTag,
|
|
MappedName* original = nullptr,
|
|
std::vector<MappedName>* history = nullptr) const;
|
|
|
|
/** Iterate through the history of the give element name with a given callback
|
|
*
|
|
* @param name: the input element name
|
|
* @param cb: trace callback with call signature.
|
|
* @sa TraceCallback
|
|
*/
|
|
void traceElement(const MappedName& name, long masterTag, TraceCallback cb) const;
|
|
|
|
|
|
private:
|
|
/** Serialize this map
|
|
* @param stream: serialized stream
|
|
* @param childMapSet: where all child element maps are stored
|
|
* @param postfixMap. where all postfixes are stored
|
|
*/
|
|
void save(std::ostream& stream,
|
|
int index,
|
|
const std::map<const ElementMap*, int>& childMapSet,
|
|
const std::map<QByteArray, int>& postfixMap) const;
|
|
|
|
/** Deserialize and restore this map.
|
|
* @param hasherRef: where all the StringIDs are stored
|
|
* @param stream: stream to deserialize
|
|
* @param childMaps: where all child element maps are stored
|
|
* @param postfixes. where all postfixes are stored
|
|
*/
|
|
ElementMapPtr restore(::App::StringHasherRef hasherRef,
|
|
std::istream& stream,
|
|
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`.*/
|
|
MappedName renameDuplicateElement(int index,
|
|
const IndexedName& element,
|
|
const IndexedName& element2,
|
|
const MappedName& name,
|
|
ElementIDRefs& sids,
|
|
long masterTag) const;
|
|
|
|
/** 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<>> 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
|