diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 77be9f9c5..9e6ecc2b0 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -58,11 +58,17 @@ recompute path. Also enables more complicated dependencies beyond trees. # include #endif +#include +#include +#include + +#if USE_OLD_DAG #include #include #include #include -#include +#endif //USE_OLD_DAG + #include #include #include @@ -138,15 +144,17 @@ struct DocumentP DocumentObject* activeObject; Transaction *activeUndoTransaction; int iTransactionMode; - std::map vertexMap; bool rollback; bool undoing; ///< document in the middle of undo or redo std::bitset<32> StatusBits; int iUndoMode; unsigned int UndoMemSize; unsigned int UndoMaxStackSize; +#if USE_OLD_DAG DependencyList DepList; std::map VertexObjectList; + std::map vertexMap; +#endif //USE_OLD_DAG DocumentP() { activeObject = 0; @@ -211,7 +219,7 @@ void Document::exportGraphviz(std::ostream& out) const { /* Typedefs for a graph with graphviz attributes */ typedef std::map GraphvizAttributes; - typedef subgraph< adjacency_list, property >, property Document::getInList(const DocumentObject* me) return result; } +#if USE_OLD_DAG namespace boost { // recursive helper function to get all dependencies void out_edges_recursive(const Vertex& v, const DependencyList& g, std::set& out) @@ -1753,29 +1762,6 @@ Document::getDependencyList(const std::vector& objs) const return ary; } -/** - * @brief Signal that object identifiers, typically a property or document object has been renamed. - * - * This function iterates through all document object in the document, and calls its - * renameObjectIdentifiers functions. - * - * @param paths Map with current and new names - */ - -void Document::renameObjectIdentifiers(const std::map &paths) -{ - std::map extendedPaths; - - std::map::const_iterator it = paths.begin(); - while (it != paths.end()) { - extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath(); - ++it; - } - - for (std::vector::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) - (*it)->renameObjectIdentifiers(extendedPaths); -} - void Document::_rebuildDependencyList(void) { d->VertexObjectList.clear(); @@ -1809,13 +1795,62 @@ void Document::_rebuildDependencyList(void) } } -void Document::recompute() +#else + +std::vector Document::getDependencyList(const std::vector& objs) const { + std::vector dep; + for (auto obj : objs){ + if(!obj) + continue; + std::vector objDep = obj->getOutListRecursive(); + dep.insert(dep.end(), objDep.begin(), objDep.end()); + dep.push_back(obj); + } + + // remove duplicate entries and resize the vector + std::sort(dep.begin(), dep.end()); + auto newEnd = std::unique(dep.begin(), dep.end()); + dep.resize(std::distance(dep.begin(), newEnd)); + + return dep; +} +#endif // USE_OLD_DAG + + +/** + * @brief Signal that object identifiers, typically a property or document object has been renamed. + * + * This function iterates through all document object in the document, and calls its + * renameObjectIdentifiers functions. + * + * @param paths Map with current and new names + */ + +void Document::renameObjectIdentifiers(const std::map &paths) +{ + std::map extendedPaths; + + std::map::const_iterator it = paths.begin(); + while (it != paths.end()) { + extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath(); + ++it; + } + + for (std::vector::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) + (*it)->renameObjectIdentifiers(extendedPaths); +} + +#if USE_OLD_DAG +int Document::recompute() +{ + int objectCount = 0; + // The 'SkipRecompute' flag can be (tmp.) set to avoid to many // time expensive recomputes bool skip = testStatus(Document::SkipRecompute); if (skip) - return; + return 0; // delete recompute log for (std::vector::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it) @@ -1835,7 +1870,7 @@ void Document::recompute() } catch (const std::exception& e) { std::cerr << "Document::recompute: " << e.what() << std::endl; - return; + return -1; } // caching vertex to DocObject @@ -1908,8 +1943,9 @@ void Document::recompute() if ( _recomputeFeature(Cur)) { // if somthing happen break execution of recompute d->vertexMap.clear(); - return; + return -1; } + ++objectCount; } } @@ -1921,8 +1957,92 @@ void Document::recompute() d->vertexMap.clear(); signalRecomputed(*this); + + return objectCount; } +#else // USE_OLD_DAG + +std::vector Document::topologicalSort() const +{ + // topological sort algorithm described here: https://de.wikipedia.org/wiki/Topologische_Sortierung#Algorithmus_f.C3.BCr_das_Topologische_Sortieren + vector < App::DocumentObject* > ret; + ret.reserve(d->objectArray.size()); + map < App::DocumentObject*,int > countMap; + + for (auto objectIt : d->objectArray) + countMap[objectIt] = objectIt->getInList().size(); + + auto rootObjeIt = find_if(countMap.begin(), countMap.end(), [](pair < App::DocumentObject*, int > count)->bool { + return count.second == 0; + }); + + if (rootObjeIt == countMap.end()){ + cerr << "Document::topologicalSort: cyclic dependency detected (no root object)" << endl; + return ret; + } + + while (rootObjeIt != countMap.end()){ + rootObjeIt->second = rootObjeIt->second - 1; + for (auto outListIt : rootObjeIt->first->getOutList()){ + auto outListMapIt = countMap.find(outListIt); + outListMapIt->second = outListMapIt->second - 1; + } + ret.push_back(rootObjeIt->first); + + rootObjeIt = find_if(countMap.begin(), countMap.end(), [](pair < App::DocumentObject*, int > count)->bool { + return count.second == 0; + }); + } + + return ret; +} + +int Document::recompute() +{ + int objectCount = 0; + // delete recompute log + for( auto LogEntry: _RecomputeLog) + delete LogEntry; + _RecomputeLog.clear(); + + // get the sorted vector of all objects in the document and go though it from the end + vector topoSortedObjects = topologicalSort(); + + if (topoSortedObjects.size() != d->objectArray.size()){ + cerr << "App::Document::recompute(): topological sort fails, invalid DAG!" << endl; + return -1; + } + + for (auto objIt = topoSortedObjects.rbegin(); objIt != topoSortedObjects.rend(); ++objIt){ + // ask the object if it should be recomputed + if ((*objIt)->mustExecute() == 1){ + objectCount++; + if (_recomputeFeature(*objIt)) { + // if something happen break execution of recompute + return -1; + } + else{ + (*objIt)->purgeTouched(); + // set all dependent object touched to force recompute + for (auto inObjIt : (*objIt)->getInList()) + inObjIt->touch(); + } + } + + } +#ifdef FC_DEBUG + // check if all objects are recalculated which were thouched + for (auto objectIt : d->objectArray) + if (objectIt->isTouched()) + cerr << "Document::recompute(): " << objectIt->getNameInDocument() << " still touched after recompute" << endl; +#endif + + return objectCount; +} + +#endif // USE_OLD_DAG + const char * Document::getErrorDescription(const App::DocumentObject*Obj) const { for (std::vector::const_iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it) @@ -2182,6 +2302,7 @@ void Document::remObject(const char* sName) signalTransactionRemove(*pos->second, 0); } +#if USE_OLD_DAG if (!d->vertexMap.empty()) { // recompute of document is running for (std::map::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) { @@ -2191,7 +2312,8 @@ void Document::remObject(const char* sName) } } } - +#endif //USE_OLD_DAG + // Before deleting we must nullify all dependant objects breakDependency(pos->second, true); @@ -2565,3 +2687,14 @@ PyObject * Document::getPyObject(void) { return Py::new_reference_to(DocumentPythonObject); } + +std::vector Document::getRootObjects() const +{ + std::vector < App::DocumentObject* > ret; + + for (auto objectIt : d->objectArray) + if (objectIt->getInList().size() == 0) + ret.push_back(objectIt); + + return ret; +} \ No newline at end of file diff --git a/src/App/Document.h b/src/App/Document.h index 3aec89831..92bc29932 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -37,8 +37,6 @@ #include #include -#include - namespace Base { class Writer; @@ -246,8 +244,8 @@ public: void setClosable(bool); /// check whether the document can be closed bool isClosable() const; - /// Recompute all touched features - void recompute(); + /// Recompute all touched features and return the amount of recalculated features + int recompute(); /// Recompute only one feature void recomputeFeature(DocumentObject* Feat); /// get the error log from the recompute run @@ -311,10 +309,15 @@ public: std::vector getInList(const DocumentObject* me) const; /// Get a complete list of all objects the given objects depend on. The list /// also contains the given objects! + /// deprecated! Use In- and OutList mimic in the DocumentObject instead! std::vector getDependencyList (const std::vector&) const; // set Changed //void setChanged(DocumentObject* change); + /// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting) + std::vector topologicalSort() const; + /// get all root objects (objects no other one reference too) + std::vector getRootObjects() const; //@} /// Function called to signal that an object identifier has been renamed @@ -352,8 +355,10 @@ protected: /// helper which Recompute only this feature bool _recomputeFeature(DocumentObject* Feat); void _clearRedos(); +#if USE_OLD_DAG /// refresh the internal dependency graph void _rebuildDependencyList(void); +#endif std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const; diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index 346c38491..7883c9146 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -182,6 +182,7 @@ std::vector DocumentObject::getOutList(void) const return ret; } +#if USE_OLD_DAG std::vector DocumentObject::getInList(void) const { if (_pDoc) @@ -190,6 +191,79 @@ std::vector DocumentObject::getInList(void) const return std::vector(); } +#else // if USE_OLD_DAG + +std::vector DocumentObject::getInList(void) const +{ + return _inList; +} + +#endif // if USE_OLD_DAG + + +void _getInListRecursive(std::vector& objSet, const DocumentObject* obj, const DocumentObject* checkObj, int depth) +{ + for (const auto objIt : obj->getInList()){ + // if the check object is in the recursive inList we have a cycle! + if (objIt == checkObj || depth <= 0){ + std::cerr << "DocumentObject::getInListRecursive(): cyclic dependency detected!"< DocumentObject::getInListRecursive(void) const +{ + // number of objects in document is a good estimate in result size + int maxDepth = getDocument()->countObjects() +2; + std::vector result; + result.reserve(maxDepth); + + // using a rcursie helper to collect all InLists + _getInListRecursive(result, this, this, maxDepth); + + // remove duplicate entries and resize the vector + std::sort(result.begin(), result.end()); + auto newEnd = std::unique(result.begin(), result.end()); + result.resize(std::distance(result.begin(), newEnd)); + + return result; +} + +void _getOutListRecursive(std::vector& objSet, const DocumentObject* obj, const DocumentObject* checkObj, int depth) +{ + for (const auto objIt : obj->getOutList()){ + // if the check object is in the recursive inList we have a cycle! + if (objIt == checkObj || depth <= 0){ + std::cerr << "DocumentObject::getOutListRecursive(): cyclic dependency detected!" << std::endl; + throw Base::Exception("DocumentObject::getOutListRecursive(): cyclic dependency detected!"); + } + objSet.push_back(objIt); + _getOutListRecursive(objSet, objIt, checkObj,depth-1); + } +} + +std::vector DocumentObject::getOutListRecursive(void) const +{ + // number of objects in document is a good estimate in result size + int maxDepth = getDocument()->countObjects() + 2; + std::vector result; + result.reserve(maxDepth); + + // using a recursive helper to collect all OutLists + _getOutListRecursive(result, this, this, maxDepth); + + // remove duplicate entries and resize the vector + std::sort(result.begin(), result.end()); + auto newEnd = std::unique(result.begin(), result.end()); + result.resize(std::distance(result.begin(), newEnd)); + + return result; +} + DocumentObjectGroup* DocumentObject::getGroup() const { return dynamic_cast(GroupExtension::getGroupOfObject(this)); @@ -229,6 +303,67 @@ bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSub &linkTo) const return this->testIfLinkDAGCompatible(linkTo_in_vector); } +#if USE_OLD_DAG +#else +bool DocumentObject::_isInInListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const +{ + if (std::find(_inList.begin(), _inList.end(), test) != _inList.end()) + return true; + + for (auto obj : _inList){ + // if the check object is in the recursive inList we have a cycle! + if (obj == checkObj || depth <= 0){ + std::cerr << "DocumentObject::getOutListRecursive(): cyclic dependency detected!" << std::endl; + throw Base::Exception("DocumentObject::getOutListRecursive(): cyclic dependency detected!"); + } + + if (_isInInListRecursive(obj, test, checkObj, depth - 1)) + return true; + } + + return false; +} + +bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const +{ + return _isInInListRecursive(this, linkTo, this, getDocument()->countObjects()); +} + +bool DocumentObject::isInInList(DocumentObject *linkTo) const +{ + if (std::find(_inList.begin(), _inList.end(), linkTo) != _inList.end()) + return true; + else + return false; +} + +bool DocumentObject::_isInOutListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const +{ + std::vector outList = act->getOutList(); + + if (std::find(outList.begin(), outList.end(), test) != outList.end()) + return true; + + for (auto obj : outList){ + // if the check object is in the recursive inList we have a cycle! + if (obj == checkObj || depth <= 0){ + std::cerr << "DocumentObject::isInOutListRecursive(): cyclic dependency detected!" << std::endl; + throw Base::Exception("DocumentObject::isInOutListRecursive(): cyclic dependency detected!"); + } + + if (_isInOutListRecursive(obj, test, checkObj, depth - 1)) + return true; + } + + return false; +} + +bool DocumentObject::isInOutListRecursive(DocumentObject *linkTo) const +{ + return _isInOutListRecursive(this, linkTo, this, getDocument()->countObjects()); +} +#endif //USE_OLD_DAG + void DocumentObject::onLostLinkToObject(DocumentObject*) { @@ -405,3 +540,17 @@ void DocumentObject::unsetupObject() for(auto ext : vector) ext->onExtendedUnsetupObject(); } + +#if USE_OLD_DAG +#else +void App::DocumentObject::_removeBackLink(DocumentObject* rmfObj) +{ + _inList.erase(std::remove(_inList.begin(), _inList.end(), rmfObj), _inList.end()); +} + +void App::DocumentObject::_addBackLink(DocumentObject* newObje) +{ + if ( std::find(_inList.begin(), _inList.end(), newObje) == _inList.end() ) + _inList.push_back(newObje); +} +#endif //USE_OLD_DAG \ No newline at end of file diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index f74785431..72f26662d 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -127,12 +127,37 @@ public: void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);} //@} + /** DAG handling + This part of the interface deals with viewing the document as + an DAG (directed acyclic graph). + */ + //@{ /// returns a list of objects this object is pointing to by Links std::vector getOutList(void) const; + /// returns a list of objects this object is pointing to by Links and all further descended + std::vector getOutListRecursive(void) const; /// get all objects link to this object std::vector getInList(void) const; + /// get all objects link directly or indirectly to this object + std::vector getInListRecursive(void) const; /// get group if object is part of a group, otherwise 0 is returned DocumentObjectGroup* getGroup() const; +#if USE_OLD_DAG +#else + /// test if this object is in the InList and recursive further down + bool isInInListRecursive(DocumentObject* objToTest) const; + /// test if this object is directly (non recursive) in the InList + bool isInInList(DocumentObject* objToTest) const; + /// test if the given object is in the OutList and recursive further down + bool isInOutListRecursive(DocumentObject* objToTest) const; + /// test if this object is directly (non recursive) in the OutList + bool isInOutList(DocumentObject* objToTest) const; + /// internal, used by ProperyLink to maintain DAG back links + void _removeBackLink(DocumentObject*); + /// internal, used by ProperyLink to maintain DAG back links + void _addBackLink(DocumentObject*); +#endif //USE_OLD_DAG + //@} /** * @brief testIfLinkIsDAG tests a link that is about to be created for @@ -149,7 +174,6 @@ public: bool testIfLinkDAGCompatible(App::PropertyLinkSubList &linksTo) const; bool testIfLinkDAGCompatible(App::PropertyLinkSub &linkTo) const; - public: /** mustExecute * We call this method to check if the object was modified to @@ -259,6 +283,15 @@ protected: // attributes // pointer to the document name string (for performance) const std::string *pcNameInDocument; + +private: + // Back pointer to all the fathers in a DAG of the document + // this is used by the document (via friend) to have a effective DAG handling + std::vector _inList; + // helper for isInInListRecursive() + bool _isInInListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const; + // helper for isInOutListRecursive() + bool _isInOutListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const; }; class AppExport ObjectStatusLocker diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index 7e774ff84..156d6e6c4 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -61,12 +61,24 @@ + + + A list of all objects this object links to recursivly. + + + A list of all objects which link to this object. + + + A list of all objects which link to this object recursivly. + + + Return the internal name of this object diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index 9951be09c..10803d735 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -207,6 +207,22 @@ Py::List DocumentObjectPy::getInList(void) const return ret; } +Py::List DocumentObjectPy::getInListRecursive(void) const +{ + Py::List ret; + try{ + + std::vector list = getDocumentObjectPtr()->getInListRecursive(); + + for (std::vector::iterator It = list.begin(); It != list.end(); ++It) + ret.append(Py::Object((*It)->getPyObject(), true)); + + }catch (const Base::Exception& e) { + throw Py::IndexError(e.what()); + } + return ret; +} + Py::List DocumentObjectPy::getOutList(void) const { Py::List ret; @@ -218,6 +234,24 @@ Py::List DocumentObjectPy::getOutList(void) const return ret; } +Py::List DocumentObjectPy::getOutListRecursive(void) const +{ + Py::List ret; + try { + + std::vector list = getDocumentObjectPtr()->getOutListRecursive(); + + // creat the python list for the output + for (std::vector::iterator It = list.begin(); It != list.end(); ++It) + ret.append(Py::Object((*It)->getPyObject(), true)); + } + catch (const Base::Exception& e) { + throw Py::IndexError(e.what()); + } + + return ret; +} + PyObject* DocumentObjectPy::setExpression(PyObject * args) { char * path = NULL; diff --git a/src/App/DocumentPy.xml b/src/App/DocumentPy.xml index 53a4bfa0a..f5afe30b7 100644 --- a/src/App/DocumentPy.xml +++ b/src/App/DocumentPy.xml @@ -151,6 +151,18 @@ Both parameters are optional. + + + The list of object of this document in topological sorted order + + + + + + The list of root object of this document + + + The Undo mode of the Document (0 = no Undo, 1 = Undo/Redo) diff --git a/src/App/DocumentPyImp.cpp b/src/App/DocumentPyImp.cpp index 7cc4b9f22..a57ce47bd 100644 --- a/src/App/DocumentPyImp.cpp +++ b/src/App/DocumentPyImp.cpp @@ -490,6 +490,38 @@ Py::List DocumentPy::getObjects(void) const return res; } +Py::List DocumentPy::getToplogicalSortedObjects(void) const +{ +#if USE_OLD_DAG == 0 + std::vector objs = getDocumentPtr()->topologicalSort(); + Py::List res; + + for (std::vector::const_iterator It = objs.begin(); It != objs.end(); ++It) + //Note: Here we must force the Py::Object to own this Python object as getPyObject() increments the counter + res.append(Py::Object((*It)->getPyObject(), true)); + + return res; +#else + return Py::List(); +#endif +} + +Py::List DocumentPy::getRootObjects(void) const +{ +#if USE_OLD_DAG == 0 + std::vector objs = getDocumentPtr()->getRootObjects(); + Py::List res; + + for (std::vector::const_iterator It = objs.begin(); It != objs.end(); ++It) + //Note: Here we must force the Py::Object to own this Python object as getPyObject() increments the counter + res.append(Py::Object((*It)->getPyObject(), true)); + + return res; +#else + return Py::List(); +#endif +} + Py::Int DocumentPy::getUndoMode(void) const { return Py::Int(getDocumentPtr()->getUndoMode()); diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index 2b71312c2..944fea6ec 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -122,10 +122,49 @@ void PropertyExpressionEngine::Paste(const Property &from) const PropertyExpressionEngine * fromee = static_cast(&from); AtomicPropertyChange signaller(*this); + +#if USE_OLD_DAG == 0 + //maintain backlinks + ExpressionMap::const_iterator i = expressions.begin(); + while (i != expressions.end()) { + std::set deps; + i->second.expression->getDeps(deps); + + std::set::const_iterator j = deps.begin(); + while (j != deps.end()) { + const ObjectIdentifier & p = *j; + DocumentObject* docObj = p.getDocumentObject(); + + if (docObj) + docObj->_removeBackLink(static_cast(getContainer())); + + ++j; + } + ++i; + } +#endif expressions.clear(); for (ExpressionMap::const_iterator it = fromee->expressions.begin(); it != fromee->expressions.end(); ++it) { expressions[it->first] = ExpressionInfo(boost::shared_ptr(it->second.expression->copy()), it->second.comment.c_str()); + +#if USE_OLD_DAG == 0 + //maintain backlinks + std::set deps; + it->second.expression->getDeps(deps); + + std::set::const_iterator j = deps.begin(); + while (j != deps.end()) { + const ObjectIdentifier & p = *j; + DocumentObject* docObj = p.getDocumentObject(); + + if (docObj) + docObj->_addBackLink(static_cast(getContainer())); + + ++j; + } +#endif + expressionChanged(it->first); } @@ -364,11 +403,43 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::sh AtomicPropertyChange signaller(*this); expressions[usePath] = ExpressionInfo(expr, comment); + +#if USE_OLD_DAG == 0 + //maintain the backlinks in the documentobject graph datastructure + std::set deps; + expr->getDeps(deps); + std::set::const_iterator j = deps.begin(); + while (j != deps.end()) { + const ObjectIdentifier & p = *j; + DocumentObject* docObj = p.getDocumentObject(); + if (docObj) + docObj->_addBackLink(static_cast(getContainer())); + + ++j; + } +#endif + expressionChanged(usePath); } else { AtomicPropertyChange signaller(*this); expressions.erase(usePath); + +#if USE_OLD_DAG == 0 + //maintain the backlinks in the documentobject graph datastructure + std::set deps; + expressions[usePath].expression->getDeps(deps); + std::set::const_iterator j = deps.begin(); + while (j != deps.end()) { + const ObjectIdentifier & p = *j; + DocumentObject* docObj = p.getDocumentObject(); + if (docObj) + docObj->_removeBackLink(static_cast(getContainer())); + + ++j; + } +#endif + expressionChanged(usePath); } } diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 4b6b9e061..de49b6cdf 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -45,7 +45,8 @@ using namespace Base; using namespace std; - +/// helper function for DAG back link handling +void _maintainBackLink(DocumentObject *me, DocumentObject *oldObj, DocumentObject *newObj); //************************************************************************** //************************************************************************** @@ -75,7 +76,17 @@ PropertyLink::~PropertyLink() void PropertyLink::setValue(App::DocumentObject * lValue) { + if (lValue == _pcLink) + return; // nothing to do + aboutToSetValue(); +#if USE_OLD_DAG==0 + // maintain the back link in the DocumentObject class + if(_pcLink) + _pcLink->_removeBackLink(static_cast(getContainer())); + if(lValue) + lValue->_addBackLink(static_cast(getContainer())); +#endif _pcLink=lValue; hasSetValue(); } @@ -163,9 +174,10 @@ Property *PropertyLink::Copy(void) const void PropertyLink::Paste(const Property &from) { - aboutToSetValue(); - _pcLink = dynamic_cast(from)._pcLink; - hasSetValue(); + if(!from.isDerivedFrom(PropertyLink::getClassTypeId())) + throw Base::Exception("Incompatible proeprty to paste to"); + + setValue(static_cast(from)._pcLink); } //************************************************************************** @@ -200,17 +212,37 @@ int PropertyLinkList::getSize(void) const void PropertyLinkList::setValue(DocumentObject* lValue) { +#if USE_OLD_DAG == 0 + //maintain the back link in the DocumentObject class + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + if(lValue) + lValue->_addBackLink(static_cast(getContainer())); +#endif + if (lValue){ aboutToSetValue(); _lValueList.resize(1); _lValueList[0] = lValue; hasSetValue(); } + else { + aboutToSetValue(); + _lValueList.clear(); + hasSetValue(); + } } void PropertyLinkList::setValues(const std::vector& lValue) { aboutToSetValue(); +#if USE_OLD_DAG == 0 + //maintain the back link in the DocumentObject class + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + for(auto *obj : lValue) + obj->_addBackLink(static_cast(getContainer())); +#endif _lValueList = lValue; hasSetValue(); } @@ -314,9 +346,7 @@ Property *PropertyLinkList::Copy(void) const void PropertyLinkList::Paste(const Property &from) { - aboutToSetValue(); - _lValueList = dynamic_cast(from)._lValueList; - hasSetValue(); + setValues(dynamic_cast(from)._lValueList); } unsigned int PropertyLinkList::getMemSize(void) const @@ -351,7 +381,16 @@ PropertyLinkSub::~PropertyLinkSub() void PropertyLinkSub::setValue(App::DocumentObject * lValue, const std::vector &SubList) { + if(lValue == _pcLinkSub) + return; //nothing to do + aboutToSetValue(); +#if USE_OLD_DAG == 0 + if(_pcLinkSub) + _pcLinkSub->_removeBackLink(static_cast(getContainer())); + if(lValue) + lValue->_addBackLink(static_cast(getContainer())); +#endif _pcLinkSub=lValue; _cSubList = SubList; hasSetValue(); @@ -505,10 +544,7 @@ Property *PropertyLinkSub::Copy(void) const void PropertyLinkSub::Paste(const Property &from) { - aboutToSetValue(); - _pcLinkSub = dynamic_cast(from)._pcLinkSub; - _cSubList = dynamic_cast(from)._cSubList; - hasSetValue(); + setValue(dynamic_cast(from)._pcLinkSub, dynamic_cast(from)._cSubList); } //************************************************************************** @@ -544,6 +580,14 @@ int PropertyLinkSubList::getSize(void) const void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName) { +#if USE_OLD_DAG == 0 + //maintain backlinks + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + if(lValue) + lValue->_addBackLink(static_cast(getContainer())); +#endif + if (lValue) { aboutToSetValue(); _lValueList.resize(1); @@ -561,9 +605,22 @@ void PropertyLinkSubList::setValue(DocumentObject* lValue,const char* SubName) } void PropertyLinkSubList::setValues(const std::vector& lValue,const std::vector& lSubNames) -{ +{ if (lValue.size() != lSubNames.size()) throw Base::Exception("PropertyLinkSubList::setValues: size of subelements list != size of objects list"); + +#if USE_OLD_DAG == 0 + //maintain backlinks. _lValueList can contain items multiple times, but we trust the document + //object to ensure that this works + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + + //maintain backlinks. lValue can contain items multiple times, but we trust the document + //object to ensure that the backlink is only added once + for(auto *obj : lValue) + obj->_addBackLink(static_cast(getContainer())); +#endif + aboutToSetValue(); _lValueList = lValue; _lSubList.resize(lSubNames.size()); @@ -577,6 +634,19 @@ void PropertyLinkSubList::setValues(const std::vector& lValue,c { if (lValue.size() != lSubNames.size()) throw Base::Exception("PropertyLinkSubList::setValues: size of subelements list != size of objects list"); + +#if USE_OLD_DAG == 0 + //maintain backlinks. _lValueList can contain items multiple times, but we trust the document + //object to ensure that this works + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + + //maintain backlinks. lValue can contain items multiple times, but we trust the document + //object to ensure that the backlink is only added once + for(auto *obj : lValue) + obj->_addBackLink(static_cast(getContainer())); +#endif + aboutToSetValue(); _lValueList = lValue; _lSubList = lSubNames; @@ -585,6 +655,18 @@ void PropertyLinkSubList::setValues(const std::vector& lValue,c void PropertyLinkSubList::setValue(DocumentObject* lValue, const std::vector &SubList) { +#if USE_OLD_DAG == 0 + //maintain backlinks. _lValueList can contain items multiple times, but we trust the document + //object to ensure that this works + for(auto *obj : _lValueList) + obj->_removeBackLink(static_cast(getContainer())); + + //maintain backlinks. lValue can contain items multiple times, but we trust the document + //object to ensure that the backlink is only added once + if(lValue) + lValue->_addBackLink(static_cast(getContainer())); +#endif + aboutToSetValue(); std::size_t size = SubList.size(); this->_lValueList.clear(); @@ -850,10 +932,7 @@ Property *PropertyLinkSubList::Copy(void) const void PropertyLinkSubList::Paste(const Property &from) { - aboutToSetValue(); - _lValueList = dynamic_cast(from)._lValueList; - _lSubList = dynamic_cast(from)._lSubList; - hasSetValue(); + setValues(dynamic_cast(from)._lValueList, dynamic_cast(from)._lSubList); } unsigned int PropertyLinkSubList::getMemSize (void) const diff --git a/src/FCConfig.h b/src/FCConfig.h index 2b89fe942..4b771bbf4 100644 --- a/src/FCConfig.h +++ b/src/FCConfig.h @@ -342,5 +342,6 @@ typedef unsigned __int64 uint64_t; //# define _PreComp_ // use precompiled header #endif +#define USE_OLD_DAG 0 #endif //FC_CONFIG_H