App: add hiddenref() expression built-in function

Any object reference inside this function is treated as hidden to
exclude it from dependency calculation. This function allows some form
of cyclic depdenency.

Merger note: renamed from "HREF" to "HIDDENREF" to avoid confusion with
the standard "hypertext reference" use of HREF.
This commit is contained in:
Zheng, Lei
2019-12-23 11:50:11 +08:00
committed by Chris Hennes
parent 8cf3cf330b
commit 14dfae0597
11 changed files with 500 additions and 225 deletions

View File

@@ -37,6 +37,7 @@
#include <boost/bind/bind.hpp>
#include <boost/graph/graph_traits.hpp>
FC_LOG_LEVEL_INIT("App",true);
using namespace App;
using namespace Base;
@@ -72,6 +73,15 @@ void PropertyExpressionContainer::slotRelabelDocument(const App::Document &doc)
///////////////////////////////////////////////////////////////////////////////////////
struct PropertyExpressionEngine::Private {
// For some reason, MSVC has trouble with vector of scoped_connection if
// defined in header, hence the private structure here.
std::vector<boost::signals2::scoped_connection> conns;
std::unordered_map<std::string, std::vector<ObjectIdentifier> > propMap;
};
///////////////////////////////////////////////////////////////////////////////////////
TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::PropertyExpressionContainer)
/**
@@ -125,7 +135,7 @@ void PropertyExpressionEngine::hasSetValue()
return;
}
std::set<App::DocumentObject*> deps;
std::map<App::DocumentObject*,bool> deps;
std::vector<std::string> labels;
unregisterElementReference();
UpdateElementReferenceExpressionVisitor<PropertyExpressionEngine> v(*this);
@@ -141,9 +151,95 @@ void PropertyExpressionEngine::hasSetValue()
updateDeps(std::move(deps));
if(pimpl) {
pimpl->conns.clear();
pimpl->propMap.clear();
}
// check if there is any hidden references
bool hasHidden = false;
for(auto &v : _Deps) {
if(v.second) {
hasHidden = true;
break;
}
}
if(hasHidden) {
if(!pimpl)
pimpl.reset(new Private);
for(auto &e : expressions) {
auto expr = e.second.expression;
if(!expr) continue;
for(auto &dep : expr->getIdentifiers()) {
if(!dep.second)
continue;
const ObjectIdentifier &var = dep.first;
for(auto &vdep : var.getDep(true)) {
auto obj = vdep.first;
auto objName = obj->getFullName() + ".";
for(auto &propName : vdep.second) {
std::string key = objName + propName;
auto &propDeps = pimpl->propMap[key];
if(propDeps.empty()) {
if(propName.size())
pimpl->conns.push_back(obj->signalChanged.connect(boost::bind(
&PropertyExpressionEngine::slotChangedProperty,this,_1,_2)));
else
pimpl->conns.push_back(obj->signalChanged.connect(boost::bind(
&PropertyExpressionEngine::slotChangedObject,this,_1,_2)));
}
propDeps.push_back(e.first);
}
}
}
}
}
PropertyExpressionContainer::hasSetValue();
}
void PropertyExpressionEngine::updateHiddenReference(const std::string &key) {
if(!pimpl)
return;
auto it = pimpl->propMap.find(key);
if(it == pimpl->propMap.end())
return;
for(auto &var : it->second) {
auto it = expressions.find(var);
if(it == expressions.end() || it->second.busy)
continue;
Property *myProp = var.getProperty();
if(!myProp)
continue;
Base::StateLocker guard(it->second.busy);
App::any value;
try {
value = it->second.expression->getValueAsAny();
if(!isAnyEqual(value, myProp->getPathValue(var)))
myProp->setPathValue(var, value);
}catch(Base::Exception &e) {
e.ReportException();
FC_ERR("Failed to evaluate property binding "
<< myProp->getFullName() << " on change of " << key);
}catch(std::bad_cast &) {
FC_ERR("Invalid type '" << value.type().name()
<< "' in property binding " << myProp->getFullName()
<< " on change of " << key);
}catch(std::exception &e) {
FC_ERR(e.what());
FC_ERR("Failed to evaluate property binding "
<< myProp->getFullName() << " on change of " << key);
}
}
}
void PropertyExpressionEngine::slotChangedObject(const App::DocumentObject &obj, const App::Property &) {
updateHiddenReference(obj.getFullName());
}
void PropertyExpressionEngine::slotChangedProperty(const App::DocumentObject &, const App::Property &prop) {
updateHiddenReference(prop.getFullName());
}
void PropertyExpressionEngine::Paste(const Property &from)
{
const PropertyExpressionEngine &fromee = dynamic_cast<const PropertyExpressionEngine&>(from);
@@ -562,17 +658,17 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute(ExecuteOption o
prop->setPathValue(*it, value);
}catch(Base::Exception &e) {
std::ostringstream ss;
ss << e.what() << std::endl << "in property binding '" << prop->getName() << "'";
ss << e.what() << std::endl << "in property binding '" << prop->getFullName() << "'";
e.setMessage(ss.str());
throw;
}catch(std::bad_cast &) {
std::ostringstream ss;
ss << "Invalid type '" << value.type().name() << "'";
ss << "\nin property binding '" << prop->getName() << "'";
ss << "\nin property binding '" << prop->getFullName() << "'";
throw Base::TypeError(ss.str().c_str());
}catch(std::exception &e) {
std::ostringstream ss;
ss << e.what() << "\nin property binding '" << prop->getName() << "'";
ss << e.what() << "\nin property binding '" << prop->getFullName() << "'";
throw Base::RuntimeError(ss.str().c_str());
}
}
@@ -610,9 +706,11 @@ void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj,
bool PropertyExpressionEngine::depsAreTouched() const
{
for(auto obj : _Deps)
if(obj->isTouched())
for(auto &v : _Deps) {
// v.second inidcates if it is a hidden reference
if(!v.second && v.first->isTouched())
return true;
}
return false;
}
@@ -639,8 +737,9 @@ std::string PropertyExpressionEngine::validateExpression(const ObjectIdentifier
assert(pathDocObj);
auto inList = pathDocObj->getInListEx(true);
for(auto docObj : expr->getDepObjects()) {
if(inList.count(docObj)) {
for(auto &v : expr->getDepObjects()) {
auto docObj = v.first;
if(!v.second && inList.count(docObj)) {
std::stringstream ss;
ss << "cyclic reference to " << docObj->getFullName();
return ss.str();
@@ -765,8 +864,8 @@ bool PropertyExpressionEngine::adjustLink(const std::set<DocumentObject*> &inLis
if(!owner)
return false;
bool found = false;
for(auto obj : _Deps) {
if(inList.count(obj)) {
for(auto &v : _Deps) {
if(inList.count(v.first)) {
found = true;
break;
}