DAG: Change object graph handling to be bidirectional

This commit is contained in:
Stefan Tröger 2016-12-25 20:14:58 +01:00 committed by wmayer
parent 93262e98b4
commit 665eb63d4c
11 changed files with 613 additions and 52 deletions

View File

@ -58,11 +58,17 @@ recompute path. Also enables more complicated dependencies beyond trees.
# include <bitset>
#endif
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/graphviz.hpp>
#if USE_OLD_DAG
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/graphviz.hpp>
#endif //USE_OLD_DAG
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <unordered_set>
@ -138,15 +144,17 @@ struct DocumentP
DocumentObject* activeObject;
Transaction *activeUndoTransaction;
int iTransactionMode;
std::map<Vertex,DocumentObject*> 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<DocumentObject*,Vertex> VertexObjectList;
std::map<Vertex,DocumentObject*> 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<std::string, std::string> GraphvizAttributes;
typedef subgraph< adjacency_list<vecS, vecS, directedS,
typedef boost::subgraph< adjacency_list<vecS, vecS, directedS,
property<vertex_attribute_t, GraphvizAttributes>,
property<edge_index_t, int, property<edge_attribute_t, GraphvizAttributes> >,
property<graph_name_t, std::string,
@ -1667,6 +1675,7 @@ std::vector<App::DocumentObject*> 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<Vertex>& out)
@ -1753,29 +1762,6 @@ Document::getDependencyList(const std::vector<App::DocumentObject*>& 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<App::ObjectIdentifier, App::ObjectIdentifier> &paths)
{
std::map<App::ObjectIdentifier, App::ObjectIdentifier> extendedPaths;
std::map<App::ObjectIdentifier, App::ObjectIdentifier>::const_iterator it = paths.begin();
while (it != paths.end()) {
extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath();
++it;
}
for (std::vector<DocumentObject*>::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<App::DocumentObject*> Document::getDependencyList(const std::vector<App::DocumentObject*>& objs) const
{
std::vector<App::DocumentObject*> dep;
for (auto obj : objs){
if(!obj)
continue;
std::vector<App::DocumentObject*> 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<App::ObjectIdentifier, App::ObjectIdentifier> &paths)
{
std::map<App::ObjectIdentifier, App::ObjectIdentifier> extendedPaths;
std::map<App::ObjectIdentifier, App::ObjectIdentifier>::const_iterator it = paths.begin();
while (it != paths.end()) {
extendedPaths[it->first.canonicalPath()] = it->second.canonicalPath();
++it;
}
for (std::vector<DocumentObject*>::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<App::DocumentObjectExecReturn*>::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<App::DocumentObject*> 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<DocumentObject*> 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<App::DocumentObjectExecReturn*>::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<Vertex,DocumentObject*>::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<App::DocumentObject*> Document::getRootObjects() const
{
std::vector < App::DocumentObject* > ret;
for (auto objectIt : d->objectArray)
if (objectIt->getInList().size() == 0)
ret.push_back(objectIt);
return ret;
}

View File

@ -37,8 +37,6 @@
#include <stack>
#include <boost/signals.hpp>
#include <boost/graph/adjacency_list.hpp>
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<App::DocumentObject*> 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<App::DocumentObject*> getDependencyList
(const std::vector<App::DocumentObject*>&) const;
// set Changed
//void setChanged(DocumentObject* change);
/// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting)
std::vector<App::DocumentObject*> topologicalSort() const;
/// get all root objects (objects no other one reference too)
std::vector<App::DocumentObject*> 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;

View File

@ -182,6 +182,7 @@ std::vector<DocumentObject*> DocumentObject::getOutList(void) const
return ret;
}
#if USE_OLD_DAG
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
{
if (_pDoc)
@ -190,6 +191,79 @@ std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
return std::vector<App::DocumentObject*>();
}
#else // if USE_OLD_DAG
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
{
return _inList;
}
#endif // if USE_OLD_DAG
void _getInListRecursive(std::vector<DocumentObject*>& 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!"<<std::endl;
throw Base::Exception("DocumentObject::getInListRecursive(): cyclic dependency detected!");
}
objSet.push_back(objIt);
_getInListRecursive(objSet, objIt, checkObj,depth-1);
}
}
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
{
// number of objects in document is a good estimate in result size
int maxDepth = getDocument()->countObjects() +2;
std::vector<App::DocumentObject*> 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<DocumentObject*>& 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<App::DocumentObject*> DocumentObject::getOutListRecursive(void) const
{
// number of objects in document is a good estimate in result size
int maxDepth = getDocument()->countObjects() + 2;
std::vector<App::DocumentObject*> 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<DocumentObjectGroup*>(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 <DocumentObject*> 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

View File

@ -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<App::DocumentObject*> getOutList(void) const;
/// returns a list of objects this object is pointing to by Links and all further descended
std::vector<App::DocumentObject*> getOutListRecursive(void) const;
/// get all objects link to this object
std::vector<App::DocumentObject*> getInList(void) const;
/// get all objects link directly or indirectly to this object
std::vector<App::DocumentObject*> 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<App::DocumentObject*> _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

View File

@ -61,12 +61,24 @@
</Documentation>
<Parameter Name="OutList" Type="List"/>
</Attribute>
<Attribute Name="OutListRecursive" ReadOnly="true">
<Documentation>
<UserDocu>A list of all objects this object links to recursivly.</UserDocu>
</Documentation>
<Parameter Name="OutListRecursive" Type="List"/>
</Attribute>
<Attribute Name="InList" ReadOnly="true">
<Documentation>
<UserDocu>A list of all objects which link to this object.</UserDocu>
</Documentation>
<Parameter Name="InList" Type="List"/>
</Attribute>
<Attribute Name="InListRecursive" ReadOnly="true">
<Documentation>
<UserDocu>A list of all objects which link to this object recursivly.</UserDocu>
</Documentation>
<Parameter Name="InListRecursive" Type="List"/>
</Attribute>
<Attribute Name="Name" ReadOnly="true">
<Documentation>
<UserDocu>Return the internal name of this object</UserDocu>

View File

@ -207,6 +207,22 @@ Py::List DocumentObjectPy::getInList(void) const
return ret;
}
Py::List DocumentObjectPy::getInListRecursive(void) const
{
Py::List ret;
try{
std::vector<DocumentObject*> list = getDocumentObjectPtr()->getInListRecursive();
for (std::vector<DocumentObject*>::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<DocumentObject*> list = getDocumentObjectPtr()->getOutListRecursive();
// creat the python list for the output
for (std::vector<DocumentObject*>::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;

View File

@ -151,6 +151,18 @@ Both parameters are optional.</UserDocu>
</Documentation>
<Parameter Name="Objects" Type="List" />
</Attribute>
<Attribute Name="ToplogicalSortedObjects" ReadOnly="true">
<Documentation>
<UserDocu>The list of object of this document in topological sorted order</UserDocu>
</Documentation>
<Parameter Name="ToplogicalSortedObjects" Type="List" />
</Attribute>
<Attribute Name="RootObjects" ReadOnly="true">
<Documentation>
<UserDocu>The list of root object of this document</UserDocu>
</Documentation>
<Parameter Name="RootObjects" Type="List" />
</Attribute>
<Attribute Name="UndoMode" ReadOnly="false">
<Documentation>
<UserDocu>The Undo mode of the Document (0 = no Undo, 1 = Undo/Redo)</UserDocu>

View File

@ -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<DocumentObject*> objs = getDocumentPtr()->topologicalSort();
Py::List res;
for (std::vector<DocumentObject*>::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<DocumentObject*> objs = getDocumentPtr()->getRootObjects();
Py::List res;
for (std::vector<DocumentObject*>::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());

View File

@ -122,10 +122,49 @@ void PropertyExpressionEngine::Paste(const Property &from)
const PropertyExpressionEngine * fromee = static_cast<const PropertyExpressionEngine*>(&from);
AtomicPropertyChange signaller(*this);
#if USE_OLD_DAG == 0
//maintain backlinks
ExpressionMap::const_iterator i = expressions.begin();
while (i != expressions.end()) {
std::set<ObjectIdentifier> deps;
i->second.expression->getDeps(deps);
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
const ObjectIdentifier & p = *j;
DocumentObject* docObj = p.getDocumentObject();
if (docObj)
docObj->_removeBackLink(static_cast<App::DocumentObject*>(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<Expression>(it->second.expression->copy()), it->second.comment.c_str());
#if USE_OLD_DAG == 0
//maintain backlinks
std::set<ObjectIdentifier> deps;
it->second.expression->getDeps(deps);
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
const ObjectIdentifier & p = *j;
DocumentObject* docObj = p.getDocumentObject();
if (docObj)
docObj->_addBackLink(static_cast<App::DocumentObject*>(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<ObjectIdentifier> deps;
expr->getDeps(deps);
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
const ObjectIdentifier & p = *j;
DocumentObject* docObj = p.getDocumentObject();
if (docObj)
docObj->_addBackLink(static_cast<App::DocumentObject*>(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<ObjectIdentifier> deps;
expressions[usePath].expression->getDeps(deps);
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
const ObjectIdentifier & p = *j;
DocumentObject* docObj = p.getDocumentObject();
if (docObj)
docObj->_removeBackLink(static_cast<App::DocumentObject*>(getContainer()));
++j;
}
#endif
expressionChanged(usePath);
}
}

View File

@ -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<DocumentObject*>(getContainer()));
if(lValue)
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
#endif
_pcLink=lValue;
hasSetValue();
}
@ -163,9 +174,10 @@ Property *PropertyLink::Copy(void) const
void PropertyLink::Paste(const Property &from)
{
aboutToSetValue();
_pcLink = dynamic_cast<const PropertyLink&>(from)._pcLink;
hasSetValue();
if(!from.isDerivedFrom(PropertyLink::getClassTypeId()))
throw Base::Exception("Incompatible proeprty to paste to");
setValue(static_cast<const PropertyLink&>(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<DocumentObject*>(getContainer()));
if(lValue)
lValue->_addBackLink(static_cast<DocumentObject*>(getContainer()));
#endif
if (lValue){
aboutToSetValue();
_lValueList.resize(1);
_lValueList[0] = lValue;
hasSetValue();
}
else {
aboutToSetValue();
_lValueList.clear();
hasSetValue();
}
}
void PropertyLinkList::setValues(const std::vector<DocumentObject*>& lValue)
{
aboutToSetValue();
#if USE_OLD_DAG == 0
//maintain the back link in the DocumentObject class
for(auto *obj : _lValueList)
obj->_removeBackLink(static_cast<DocumentObject*>(getContainer()));
for(auto *obj : lValue)
obj->_addBackLink(static_cast<DocumentObject*>(getContainer()));
#endif
_lValueList = lValue;
hasSetValue();
}
@ -314,9 +346,7 @@ Property *PropertyLinkList::Copy(void) const
void PropertyLinkList::Paste(const Property &from)
{
aboutToSetValue();
_lValueList = dynamic_cast<const PropertyLinkList&>(from)._lValueList;
hasSetValue();
setValues(dynamic_cast<const PropertyLinkList&>(from)._lValueList);
}
unsigned int PropertyLinkList::getMemSize(void) const
@ -351,7 +381,16 @@ PropertyLinkSub::~PropertyLinkSub()
void PropertyLinkSub::setValue(App::DocumentObject * lValue, const std::vector<std::string> &SubList)
{
if(lValue == _pcLinkSub)
return; //nothing to do
aboutToSetValue();
#if USE_OLD_DAG == 0
if(_pcLinkSub)
_pcLinkSub->_removeBackLink(static_cast<App::DocumentObject*>(getContainer()));
if(lValue)
lValue->_addBackLink(static_cast<App::DocumentObject*>(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<const PropertyLinkSub&>(from)._pcLinkSub;
_cSubList = dynamic_cast<const PropertyLinkSub&>(from)._cSubList;
hasSetValue();
setValue(dynamic_cast<const PropertyLinkSub&>(from)._pcLinkSub, dynamic_cast<const PropertyLinkSub&>(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<DocumentObject*>(getContainer()));
if(lValue)
lValue->_addBackLink(static_cast<DocumentObject*>(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<DocumentObject*>& lValue,const std::vector<const char*>& 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<DocumentObject*>(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<DocumentObject*>(getContainer()));
#endif
aboutToSetValue();
_lValueList = lValue;
_lSubList.resize(lSubNames.size());
@ -577,6 +634,19 @@ void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& 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<DocumentObject*>(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<DocumentObject*>(getContainer()));
#endif
aboutToSetValue();
_lValueList = lValue;
_lSubList = lSubNames;
@ -585,6 +655,18 @@ void PropertyLinkSubList::setValues(const std::vector<DocumentObject*>& lValue,c
void PropertyLinkSubList::setValue(DocumentObject* lValue, const std::vector<string> &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<DocumentObject*>(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<DocumentObject*>(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<const PropertyLinkSubList&>(from)._lValueList;
_lSubList = dynamic_cast<const PropertyLinkSubList&>(from)._lSubList;
hasSetValue();
setValues(dynamic_cast<const PropertyLinkSubList&>(from)._lValueList, dynamic_cast<const PropertyLinkSubList&>(from)._lSubList);
}
unsigned int PropertyLinkSubList::getMemSize (void) const

View File

@ -342,5 +342,6 @@ typedef unsigned __int64 uint64_t;
//# define _PreComp_ // use precompiled header
#endif
#define USE_OLD_DAG 0
#endif //FC_CONFIG_H