Make Group searching robust for cyclic dependencies. fixes #0002567

This commit is contained in:
Stefan Tröger
2017-08-04 07:06:58 +02:00
committed by wmayer
parent f34c77211a
commit 96f8d944f8
3 changed files with 66 additions and 19 deletions

View File

@@ -167,15 +167,28 @@ DocumentObject *GroupExtension::getObject(const char *Name) const
bool GroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
{
if(obj == getExtendedObject())
return false;
const std::vector<DocumentObject*>& grp = Group.getValues();
for (std::vector<DocumentObject*>::const_iterator it = grp.begin(); it != grp.end(); ++it) {
if (*it == obj) {
for (auto child : grp) {
if(!child)
continue;
if (child == obj) {
return true;
} else if ( recursive && (*it)->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
} else if (child == getExtendedObject()) {
Base::Exception("Cyclic dependencies detected: Search cannot be performed");
} else if ( recursive && child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
App::GroupExtension *subGroup = static_cast<App::GroupExtension *> (
(*it)->getExtension(GroupExtension::getExtensionClassTypeId()));
child->getExtension(GroupExtension::getExtensionClassTypeId()));
std::vector<const GroupExtension*> history;
history.push_back(this);
if (subGroup->hasObject (obj, recursive)) {
if (subGroup->recursiveHasObject (obj, subGroup, history)) {
return true;
}
}
@@ -184,22 +197,45 @@ bool GroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
return false;
}
bool GroupExtension::isChildOf(const GroupExtension* group) const
{
const std::vector<DocumentObject*>& grp = group->Group.getValues();
for (std::vector<DocumentObject*>::const_iterator it = grp.begin(); it != grp.end(); ++it) {
if (*it == getExtendedObject())
return true;
if ((*it)->hasExtension(GroupExtension::getExtensionClassTypeId())) {
if (this->isChildOf(static_cast<GroupExtension*>((*it)->getExtension(GroupExtension::getExtensionClassTypeId()))))
bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupExtension* group,
std::vector< const GroupExtension* > history) const {
//the purpose is to prevent infinite recursion when groups form a cyclic graph. To do this
//we store every group we processed on the current leave of the tree, and if we reach an
//already processed group we know that it not really is a tree but a cycle.
history.push_back(this);
for (auto child : group->Group.getValues()) {
if(!child)
continue;
if (child == obj) {
return true;
} else if ( child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
auto ext = child->getExtensionByType<GroupExtension>();
if(std::find(history.begin(), history.end(), ext) != history.end())
Base::Exception("Cyclic dependencies detected: Search cannot be performed");
if (recursiveHasObject(obj, ext, history)) {
return true;
}
}
}
return false;
}
bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) const
{
return group->hasObject(getExtendedObject(), recursive);
}
std::vector<DocumentObject*> GroupExtension::getObjects() const
{
return Group.getValues();

View File

@@ -80,10 +80,10 @@ public:
*/
bool hasObject(const DocumentObject* obj, bool recursive=false) const;
/**
* Checks whether this group object is a child (or sub-child)
* Checks whether this group object is a child (or sub-child if enabled)
* of the given group object.
*/
bool isChildOf(const GroupExtension*) const;
bool isChildOf(const GroupExtension* group, bool recursive = true) const;
/** Returns a list of all objects this group does have.
*/
std::vector<DocumentObject*> getObjects() const;
@@ -110,6 +110,9 @@ public:
private:
void removeObjectFromDocument(DocumentObject*);
//this version if has object stores the already searched objects to prevent infinite recursion
//in case of a cyclic group graph
bool recursiveHasObject(const DocumentObject* obj, const GroupExtension* group, std::vector<const GroupExtension*> history) const;
};

View File

@@ -933,9 +933,17 @@ class DocumentGroupCases(unittest.TestCase):
prt1.addObject(prt2)
grp = prt2.Group
grp.append(prt1)
prt2.Group = grp
prt1.Group = [prt1]
prt2.Group = [prt2]
prt2.Group = grp
self.Doc.recompute()
prt2.Group = []
try:
prt2.Group = [prt2]
except:
pass
else:
self.fail("Exception is expected")
self.Doc.recompute()
def tearDown(self):
# closing doc