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 7e272d00f8
commit fdae470c1b
11 changed files with 500 additions and 225 deletions

View File

@@ -444,13 +444,14 @@ void Document::exportGraphviz(std::ostream& out) const
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
auto i = expressions.begin();
while (i != expressions.end()) {
std::set<ObjectIdentifier> deps;
std::map<ObjectIdentifier,bool> deps;
i->second->getIdentifiers(deps);
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
DocumentObject * o = j->getDocumentObject();
for(auto j=deps.begin(); j!=deps.end(); ++j) {
if(j->second)
continue;
DocumentObject * o = j->first.getDocumentObject();
// Doesn't exist already?
if (o && !GraphList[o]) {
@@ -469,7 +470,6 @@ void Document::exportGraphviz(std::ostream& out) const
}
}
++j;
}
++i;
}
@@ -554,24 +554,23 @@ void Document::exportGraphviz(std::ostream& out) const
while (i != expressions.end()) {
// Get dependencies
std::set<ObjectIdentifier> deps;
std::map<ObjectIdentifier,bool> deps;
i->second->getIdentifiers(deps);
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
DocumentObject * depObjDoc = j->getDocumentObject();
std::map<std::string, Vertex>::const_iterator k = GlobalVertexList.find(getId(*j));
for(auto j=deps.begin(); j!=deps.end(); ++j) {
if(j->second)
continue;
DocumentObject * depObjDoc = j->first.getDocumentObject();
std::map<std::string, Vertex>::const_iterator k = GlobalVertexList.find(getId(j->first));
if (k == GlobalVertexList.end()) {
Graph * depSgraph = GraphList[depObjDoc] ? GraphList[depObjDoc] : &DepList;
LocalVertexList[getId(*j)] = add_vertex(*depSgraph);
GlobalVertexList[getId(*j)] = vertex_no++;
setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(*j)], j->getPropertyName() + j->getSubPathStr());
LocalVertexList[getId(j->first)] = add_vertex(*depSgraph);
GlobalVertexList[getId(j->first)] = vertex_no++;
setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(j->first)], j->first.getPropertyName() + j->first.getSubPathStr());
}
++j;
}
++i;
}
@@ -703,17 +702,18 @@ void Document::exportGraphviz(std::ostream& out) const
auto i = expressions.begin();
while (i != expressions.end()) {
std::set<ObjectIdentifier> deps;
std::map<ObjectIdentifier,bool> deps;
i->second->getIdentifiers(deps);
// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
std::set<ObjectIdentifier>::const_iterator k = deps.begin();
while (k != deps.end()) {
DocumentObject * depObjDoc = k->getDocumentObject();
for(auto k=deps.begin(); k!=deps.end(); ++k) {
if(k->second)
continue;
DocumentObject * depObjDoc = k->first.getDocumentObject();
Edge edge;
bool inserted;
tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(*k)], DepList);
tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(k->first)], DepList);
// Add this edge to the set of all expression generated edges
existingEdges.insert(std::make_pair(docObj, depObjDoc));
@@ -721,7 +721,6 @@ void Document::exportGraphviz(std::ostream& out) const
// Edges between properties should be a bit smaller, and dashed
edgeAttrMap[edge]["arrowsize"] = "0.5";
edgeAttrMap[edge]["style"] = "dashed";
++k;
}
++i;
}

View File

@@ -240,17 +240,7 @@ std::string unquote(const std::string & input)
//
// ExpressionVistor
//
void ExpressionVisitor::getDeps(Expression &e, ExpressionDeps &deps) {
e._getDeps(deps);
}
void ExpressionVisitor::getDepObjects(Expression &e,
std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels)
{
e._getDepObjects(deps,labels);
}
void ExpressionVisitor::getIdentifiers(Expression &e, std::set<App::ObjectIdentifier> &ids) {
void ExpressionVisitor::getIdentifiers(Expression &e, std::map<App::ObjectIdentifier,bool> &ids) {
e._getIdentifiers(ids);
}
@@ -905,58 +895,52 @@ Expression * Expression::parse(const DocumentObject *owner, const std::string &b
return ExpressionParser::parse(owner, buffer.c_str());
}
class GetDepsExpressionVisitor : public ExpressionVisitor {
public:
GetDepsExpressionVisitor(ExpressionDeps &deps)
:deps(deps)
{}
virtual void visit(Expression &e) {
this->getDeps(e,deps);
void Expression::getDeps(ExpressionDeps &deps, int option) const {
for(auto &v : getIdentifiers()) {
bool hidden = v.second;
const ObjectIdentifier &var = v.first;
if((hidden && option==DepNormal)
|| (!hidden && option==DepHidden))
continue;
for(auto &dep : var.getDep(true)) {
DocumentObject *obj = dep.first;
for(auto &propName : dep.second) {
deps[obj][propName].push_back(var);
}
}
}
ExpressionDeps &deps;
};
void Expression::getDeps(ExpressionDeps &deps) const {
GetDepsExpressionVisitor v(deps);
const_cast<Expression*>(this)->visit(v);
}
ExpressionDeps Expression::getDeps() const {
ExpressionDeps Expression::getDeps(int option) const {
ExpressionDeps deps;
getDeps(deps);
getDeps(deps, option);
return deps;
}
class GetDepObjsExpressionVisitor : public ExpressionVisitor {
public:
GetDepObjsExpressionVisitor(std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels)
:deps(deps),labels(labels)
{}
virtual void visit(Expression &e) {
this->getDepObjects(e,deps,labels);
void Expression::getDepObjects(
std::map<App::DocumentObject*,bool> &deps, std::vector<std::string> *labels) const
{
for(auto &v : getIdentifiers()) {
bool hidden = v.second;
const ObjectIdentifier &var = v.first;
for(auto &dep : var.getDep(false,labels)) {
DocumentObject *obj = dep.first;
auto res = deps.insert(std::make_pair(obj,hidden));
if(!hidden || res.second)
res.first->second = hidden;
}
}
std::set<App::DocumentObject*> &deps;
std::vector<std::string> *labels;
};
void Expression::getDepObjects(std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels) const {
GetDepObjsExpressionVisitor v(deps,labels);
const_cast<Expression *>(this)->visit(v);
}
std::set<App::DocumentObject*> Expression::getDepObjects(std::vector<std::string> *labels) const {
std::set<App::DocumentObject*> deps;
std::map<App::DocumentObject*,bool> Expression::getDepObjects(std::vector<std::string> *labels) const {
std::map<App::DocumentObject*,bool> deps;
getDepObjects(deps,labels);
return deps;
}
class GetIdentifiersExpressionVisitor : public ExpressionVisitor {
public:
GetIdentifiersExpressionVisitor(std::set<App::ObjectIdentifier> &deps)
GetIdentifiersExpressionVisitor(std::map<App::ObjectIdentifier,bool> &deps)
:deps(deps)
{}
@@ -964,16 +948,16 @@ public:
this->getIdentifiers(e,deps);
}
std::set<App::ObjectIdentifier> &deps;
std::map<App::ObjectIdentifier,bool> &deps;
};
void Expression::getIdentifiers(std::set<App::ObjectIdentifier> &deps) const {
void Expression::getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const {
GetIdentifiersExpressionVisitor v(deps);
const_cast<Expression*>(this)->visit(v);
}
std::set<App::ObjectIdentifier> Expression::getIdentifiers() const {
std::set<App::ObjectIdentifier> deps;
std::map<App::ObjectIdentifier,bool> Expression::getIdentifiers() const {
std::map<App::ObjectIdentifier,bool> deps;
getIdentifiers(deps);
return deps;
}
@@ -1016,7 +1000,7 @@ ExpressionPtr Expression::importSubNames(const std::map<std::string,std::string>
if(!owner || !owner->getDocument())
return 0;
ObjectIdentifier::SubNameMap subNameMap;
for(auto &dep : getDeps()) {
for(auto &dep : getDeps(DepAll)) {
for(auto &info : dep.second) {
for(auto &path : info.second) {
auto obj = path.getDocumentObject();
@@ -1064,7 +1048,8 @@ ExpressionPtr Expression::updateLabelReference(
if(ref.size()<=2)
return ExpressionPtr();
std::vector<std::string> labels;
getDepObjects(&labels);
for(auto &v : getIdentifiers())
v.first.getDepLabels(labels);
for(auto &label : labels) {
// ref contains something like $label. and we need to strip '$' and '.'
if(ref.compare(1,ref.size()-2,label)==0) {
@@ -1709,6 +1694,32 @@ bool OperatorExpression::isRightAssociative() const
TYPESYSTEM_SOURCE(App::FunctionExpression, App::UnitExpression)
static int _HiddenReference;
struct HiddenReference {
HiddenReference(bool cond)
:cond(cond)
{
if(cond)
++_HiddenReference;
}
~HiddenReference() {
if(cond)
--_HiddenReference;
}
static bool check(int option) {
return (option==Expression::DepNormal && _HiddenReference)
|| (option==Expression::DepHidden && !_HiddenReference);
}
static bool isHidden() {
return _HiddenReference!=0;
}
bool cond;
};
FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f, std::string &&name, std::vector<Expression *> _args)
: UnitExpression(_owner)
, f(_f)
@@ -1736,6 +1747,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
case FLOOR:
case MINVERT:
case STR:
case HIDDENREF:
if (args.size() != 1)
EXPR_THROW("Invalid number of arguments: exactly one required.");
break;
@@ -2087,6 +2099,8 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
return res;
} else if (f == STR) {
return Py::String(args[0]->getPyValue().as_string());
} else if (f == HIDDENREF) {
return args[0]->getPyValue();
}
Py::Object e1 = args[0]->getPyValue();
@@ -2219,7 +2233,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
unit = v1.getUnit();
break;
default:
_EXPR_THROW("Unknown function: " << f,expr);
_EXPR_THROW("Unknown function: " << f,0);
}
/* Compute result */
@@ -2299,7 +2313,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
output = floor(value);
break;
default:
_EXPR_THROW("Unknown function: " << f,expr);
_EXPR_THROW("Unknown function: " << f,0);
}
return Py::asObject(new QuantityPy(new Quantity(scaler * output, unit)));
@@ -2421,6 +2435,8 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
ss << "create("; break;;
case STR:
ss << "str("; break;;
case HIDDENREF:
ss << "hiddenref("; break;;
default:
ss << fname << "("; break;;
}
@@ -2454,6 +2470,7 @@ void FunctionExpression::_visit(ExpressionVisitor &v)
{
std::vector<Expression*>::const_iterator i = args.begin();
HiddenReference ref(f == HIDDENREF);
while (i != args.end()) {
(*i)->visit(v);
++i;
@@ -2592,24 +2609,12 @@ Expression *VariableExpression::_copy() const
return new VariableExpression(owner, var);
}
void VariableExpression::_getDeps(ExpressionDeps &deps) const
void VariableExpression::_getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const
{
auto dep = var.getDep();
if(dep.first)
deps[dep.first][dep.second].push_back(var);
}
void VariableExpression::_getDepObjects(
std::set<App::DocumentObject*> &deps, std::vector<std::string> *labels) const
{
auto dep = var.getDep(labels);
if(dep.first)
deps.insert(dep.first);
}
void VariableExpression::_getIdentifiers(std::set<App::ObjectIdentifier> &deps) const
{
deps.insert(var);
bool hidden = HiddenReference::isHidden();
auto res = deps.insert(std::make_pair(var,hidden));
if(!hidden || res.second)
res.first->second = hidden;
}
bool VariableExpression::_relabeledDocument(const std::string &oldName,
@@ -2977,16 +2982,19 @@ Expression *RangeExpression::simplify() const
return copy();
}
void RangeExpression::_getDeps(ExpressionDeps &deps) const
void RangeExpression::_getIdentifiers(std::map<App::ObjectIdentifier,bool> &deps) const
{
bool hidden = HiddenReference::isHidden();
assert(owner);
Range i(getRange());
auto &dep = deps[owner];
do {
std::string address = i.address();
dep[address].push_back(ObjectIdentifier(owner,address));
ObjectIdentifier var(owner,i.address());
auto res = deps.insert(std::make_pair(var,hidden));
if(!hidden || res.second)
res.first->second = hidden;
} while (i.next());
}
@@ -3243,6 +3251,7 @@ static void initParser(const App::DocumentObject *owner)
registered_functions["minvert"] = FunctionExpression::MINVERT;
registered_functions["create"] = FunctionExpression::CREATE;
registered_functions["str"] = FunctionExpression::STR;
registered_functions["hiddenref"] = FunctionExpression::HIDDENREF;
// Aggregates
registered_functions["sum"] = FunctionExpression::SUM;

View File

@@ -51,7 +51,9 @@ typedef std::unique_ptr<Expression> ExpressionPtr;
AppExport bool isAnyEqual(const App::any &v1, const App::any &v2);
AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = 0);
// Map of depending objects to a map of depending property name to the full referencing object identifier
typedef std::map<App::DocumentObject*, std::map<std::string, std::vector<ObjectIdentifier> > > ExpressionDeps;
class AppExport ExpressionVisitor {
public:
virtual ~ExpressionVisitor() {}
@@ -62,9 +64,7 @@ public:
virtual App::PropertyLinkBase* getPropertyLink() {return 0;}
protected:
void getIdentifiers(Expression &e, std::set<App::ObjectIdentifier> &);
void getDeps(Expression &e, ExpressionDeps &);
void getDepObjects(Expression &e, std::set<App::DocumentObject*> &, std::vector<std::string> *);
void getIdentifiers(Expression &e, std::map<App::ObjectIdentifier, bool> &);
bool adjustLinks(Expression &e, const std::set<App::DocumentObject*> &inList);
bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName);
bool renameObjectIdentifier(Expression &e,
@@ -135,14 +135,19 @@ public:
virtual int priority() const;
void getIdentifiers(std::set<App::ObjectIdentifier> &) const;
std::set<App::ObjectIdentifier> getIdentifiers() const;
void getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const;
std::map<App::ObjectIdentifier,bool> getIdentifiers() const;
void getDeps(ExpressionDeps &deps) const;
ExpressionDeps getDeps() const;
enum DepOption {
DepNormal,
DepHidden,
DepAll,
};
void getDeps(ExpressionDeps &deps, int option=DepNormal) const;
ExpressionDeps getDeps(int option=DepNormal) const;
std::set<App::DocumentObject*> getDepObjects(std::vector<std::string> *labels=0) const;
void getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *labels=0) const;
std::map<App::DocumentObject*,bool> getDepObjects(std::vector<std::string> *labels=0) const;
void getDepObjects(std::map<App::DocumentObject*,bool> &, std::vector<std::string> *labels=0) const;
ExpressionPtr importSubNames(const std::map<std::string,std::string> &nameMap) const;
@@ -188,9 +193,7 @@ protected:
virtual bool _isIndexable() const {return false;}
virtual Expression *_copy() const = 0;
virtual void _toString(std::ostream &ss, bool persistent, int indent=0) const = 0;
virtual void _getDeps(ExpressionDeps &) const {}
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const {}
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const {}
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const {}
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &) {return false;}
virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &) {return false;}
virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &) {return false;}

View File

@@ -273,6 +273,7 @@ public:
MINVERT, // invert matrix/placement/rotation
CREATE, // create new object of a given type
STR, // stringify
HIDDENREF, // hidden reference that has no dependency check
// Aggregates
AGGREGATES,
@@ -345,9 +346,7 @@ protected:
virtual Py::Object _getPyValue() const override;
virtual void _toString(std::ostream &ss, bool persistent, int indent) const override;
virtual bool _isIndexable() const override;
virtual void _getDeps(ExpressionDeps &) const override;
virtual void _getDepObjects(std::set<App::DocumentObject*> &, std::vector<std::string> *) const override;
virtual void _getIdentifiers(std::set<App::ObjectIdentifier> &) const override;
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const override;
virtual bool _adjustLinks(const std::set<App::DocumentObject*> &, ExpressionVisitor &) override;
virtual void _importSubNames(const ObjectIdentifier::SubNameMap &) override;
virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *) override;
@@ -435,7 +434,7 @@ protected:
virtual Expression * _copy() const override;
virtual void _toString(std::ostream &ss, bool persistent, int indent) const override;
virtual Py::Object _getPyValue() const override;
virtual void _getDeps(ExpressionDeps &) const override;
virtual void _getIdentifiers(std::map<App::ObjectIdentifier,bool> &) const override;
virtual bool _renameObjectIdentifier(const std::map<ObjectIdentifier,ObjectIdentifier> &,
const ObjectIdentifier &, ExpressionVisitor &) override;
virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &) override;

View File

@@ -33,18 +33,19 @@
/// Here the FreeCAD includes sorted by Base,App,Gui......
#include <Base/GeometryPyCXX.h>
#include <App/ComplexGeoData.h>
#include <Base/Tools.h>
#include <Base/Interpreter.h>
#include <Base/QuantityPy.h>
#include <Base/Console.h>
#include <App/DocumentObjectPy.h>
#include "ComplexGeoData.h"
#include "Property.h"
#include "Application.h"
#include "Document.h"
#include "DocumentObject.h"
#include "ObjectIdentifier.h"
#include "ExpressionParser.h"
#include <Base/Tools.h>
#include <Base/Interpreter.h>
#include <Base/QuantityPy.h>
#include <App/Link.h>
#include <Base/Console.h>
#include "Link.h"
FC_LOG_LEVEL_INIT("Expression",true,true)
@@ -1086,32 +1087,58 @@ enum PseudoPropertyType {
PseudoCadquery,
};
std::pair<DocumentObject*,std::string> ObjectIdentifier::getDep(std::vector<std::string> *labels) const {
void ObjectIdentifier::getDepLabels(std::vector<std::string> &labels) const {
getDepLabels(ResolveResults(*this),labels);
}
void ObjectIdentifier::getDepLabels(
const ResolveResults &result, std::vector<std::string> &labels) const
{
if(documentObjectName.getString().size()) {
if(documentObjectName.isRealString())
labels.push_back(documentObjectName.getString());
} else if(result.propertyIndex == 1)
labels.push_back(components[0].name.getString());
if(subObjectName.getString().size())
PropertyLinkBase::getLabelReferences(labels,subObjectName.getString().c_str());
}
ObjectIdentifier::Dependencies
ObjectIdentifier::getDep(bool needProps, std::vector<std::string> *labels) const
{
Dependencies deps;
getDep(deps,needProps,labels);
return deps;
}
void ObjectIdentifier::getDep(
Dependencies &deps, bool needProps, std::vector<std::string> *labels) const
{
ResolveResults result(*this);
if(labels) {
if(documentObjectName.getString().size()) {
if(documentObjectName.isRealString())
labels->push_back(documentObjectName.getString());
} else if(result.propertyIndex == 1)
labels->push_back(components[0].name.getString());
if(subObjectName.getString().size())
PropertyLinkBase::getLabelReferences(*labels,subObjectName.getString().c_str());
if(labels)
getDepLabels(result,*labels);
if(!result.resolvedDocumentObject)
return;
if(!needProps) {
deps[result.resolvedDocumentObject];
return;
}
if(subObjectName.getString().empty()) {
if(result.propertyType==PseudoNone) {
CellAddress addr;
if(addr.parseAbsoluteAddress(result.propertyName.c_str()))
return std::make_pair(result.resolvedDocumentObject,addr.toString(true));
return std::make_pair(result.resolvedDocumentObject,result.propertyName);
}else if(result.propertyType == PseudoSelf
&& result.resolvedDocumentObject
&& result.propertyIndex+1 < (int)components.size())
{
return std::make_pair(result.resolvedDocumentObject,
components[result.propertyIndex+1].getName());
}
if(!result.resolvedProperty) {
if(result.propertyName.size())
deps[result.resolvedDocumentObject].insert(result.propertyName);
return;
}
Base::PyGILStateLocker lock;
try {
access(result,0,&deps);
}catch(Py::Exception &) {
Base::PyException e;
}catch(...){
}
return std::make_pair(result.resolvedDocumentObject,std::string());
}
/**
@@ -1241,7 +1268,7 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj,
if(!obj)
return 0;
static std::map<std::string,int> _props = {
static std::unordered_map<const char*,int,CStringHasher,CStringHasher> _props = {
{"_shape",PseudoShape},
{"_pla",PseudoPlacement},
{"_matrix",PseudoMatrix},
@@ -1270,23 +1297,8 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj,
}
return &const_cast<App::DocumentObject*>(obj)->Label; //fake the property
}
auto prop = obj->getPropertyByName(propertyName);
if(prop && !prop->testStatus(Property::Hidden) && !(prop->getType() & PropertyType::Prop_Hidden))
return prop;
auto linked = obj->getLinkedObject(true);
if(!linked || linked==obj) {
auto ext = obj->getExtensionByType<App::LinkBaseExtension>(true);
if(!ext)
return prop;
linked = ext->getTrueLinkedObject(true);
if(!linked || linked==obj)
return prop;
}
auto linkedProp = linked->getPropertyByName(propertyName);
return linkedProp?linkedProp:prop;
return obj->getPropertyByName(propertyName);
}
@@ -1503,7 +1515,8 @@ void ObjectIdentifier::String::checkImport(const App::DocumentObject *owner,
}
}
Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *value) const
Py::Object ObjectIdentifier::access(const ResolveResults &result,
Py::Object *value, Dependencies *deps) const
{
if(!result.resolvedDocumentObject || !result.resolvedProperty ||
(subObjectName.getString().size() && !result.resolvedSubObject))
@@ -1647,13 +1660,65 @@ Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *va
break;
}}}
}
auto setPropDep = [deps](DocumentObject *obj, Property *prop, const char *propName) {
if(!deps || !obj)
return;
if(prop && prop->getContainer()!=obj) {
auto linkTouched = Base::freecad_dynamic_cast<PropertyBool>(
obj->getPropertyByName("_LinkTouched"));
if(linkTouched)
propName = linkTouched->getName();
else {
auto propOwner = Base::freecad_dynamic_cast<DocumentObject>(prop->getContainer());
if(propOwner)
obj = propOwner;
else
propName = 0;
}
}
auto &propset = (*deps)[obj];
// inserting a null name in the propset indicates the dependency is
// on all properties of the corresponding object.
if(propset.size()!=1 || propset.begin()->size()) {
if(!propName)
propset.clear();
propset.insert(propName);
}
return;
};
App::DocumentObject *lastObj = result.resolvedDocumentObject;
if(result.resolvedSubObject) {
setPropDep(lastObj,0,0);
lastObj = result.resolvedSubObject;
}
if(ptype == PseudoNone)
setPropDep(lastObj, result.resolvedProperty, result.resolvedProperty->getName());
else
setPropDep(lastObj,0,0);
lastObj = 0;
if(components.empty())
return pyobj;
size_t count = components.size();
if(value) --count;
assert(idx<=count);
for(;idx<count;++idx)
for(;idx<count;++idx) {
if(PyObject_TypeCheck(*pyobj, &DocumentObjectPy::Type))
lastObj = static_cast<DocumentObjectPy*>(*pyobj)->getDocumentObjectPtr();
else if(lastObj) {
const char *attr = components[idx].getName().c_str();
auto prop = lastObj->getPropertyByName(attr);
if(!prop && pyobj.hasAttr(attr))
attr = 0;
setPropDep(lastObj,prop,attr);
lastObj = 0;
}
pyobj = components[idx].get(pyobj);
}
if(value) {
components[idx].set(pyobj,*value);
return Py::Object();

View File

@@ -342,7 +342,45 @@ public:
bool relabeledDocument(ExpressionVisitor &v, const std::string &oldLabel, const std::string &newLabel);
std::pair<App::DocumentObject*,std::string> getDep(std::vector<std::string> *labels=0) const;
/** Type for storing dependency of an ObjectIdentifier
*
* The dependency is a map from document object to a set of property names.
* An object identifier may references multiple objects using syntax like
* 'Part.Group[0].Width'.
*
* Also, we use set of string instead of set of Property pointer, because
* the property may not exist at the time this ObjectIdentifier is
* constructed.
*/
typedef std::map<App::DocumentObject *, std::set<std::string> > Dependencies;
/** Get dependencies of this object identifier
*
* @param needProps: whether need property dependencies.
* @param labels: optional return of any label references.
*
* In case of multi-object references, like 'Part.Group[0].Width', if no
* property dependency is required, then this function will only return the
* first referred object dependency. Or else, all object and property
* dependencies will be returned.
*/
Dependencies getDep(bool needProps, std::vector<std::string> *labels=0) const;
/** Get dependencies of this object identifier
*
* @param deps: returns the depdenencies.
* @param needProps: whether need property dependencies.
* @param labels: optional return of any label references.
*
* In case of multi-object references, like 'Part.Group[0].Width', if no
* property dependency is required, then this function will only return the
* first referred object dependency. Or else, all object and property
* dependencies will be returned.
*/
void getDep(Dependencies &deps, bool needProps, std::vector<std::string> *labels=0) const;
/// Returns all label references
void getDepLabels(std::vector<std::string> &labels) const;
App::Document *getDocument(String name = String(), bool *ambiguous=0) const;
@@ -422,7 +460,8 @@ protected:
void getSubPathStr(std::ostream &ss, const ResolveResults &result, bool toPython=false) const;
Py::Object access(const ResolveResults &rs, Py::Object *value=0) const;
Py::Object access(const ResolveResults &rs,
Py::Object *value=0, Dependencies *deps=0) const;
void resolve(ResolveResults & results) const;
void resolveAmbiguity(ResolveResults &results);
@@ -430,6 +469,8 @@ protected:
static App::DocumentObject *getDocumentObject(
const App::Document *doc, const String &name, std::bitset<32> &flags);
void getDepLabels(const ResolveResults &result, std::vector<std::string> &labels) const;
App::DocumentObject * owner;
String documentName;
String documentObjectName;

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;
}

View File

@@ -85,9 +85,11 @@ public:
struct ExpressionInfo {
std::shared_ptr<App::Expression> expression; /**< The actual expression tree */
bool busy;
ExpressionInfo(std::shared_ptr<App::Expression> expression = std::shared_ptr<App::Expression>()) {
this->expression = expression;
this->busy = false;
}
ExpressionInfo(const ExpressionInfo & other) {
@@ -186,6 +188,10 @@ private:
boost::unordered_map<int, App::ObjectIdentifier> &revNodes,
DiGraph &g, ExecuteOption option=ExecuteAll) const;
void slotChangedObject(const App::DocumentObject &obj, const App::Property &prop);
void slotChangedProperty(const App::DocumentObject &obj, const App::Property &prop);
void updateHiddenReference(const std::string &key);
bool running; /**< Boolean used to avoid loops */
bool restoring = false;
@@ -201,6 +207,9 @@ private:
/**< Expressions are read from file to this map first before they are validated and inserted into the actual map */
std::unique_ptr<std::vector<RestoredExpression> > restoredExpressions;
struct Private;
std::unique_ptr<Private> pimpl;
friend class AtomicPropertyChange;
};

View File

@@ -4483,7 +4483,7 @@ void PropertyXLinkContainer::afterRestore() {
if(info.docLabel != obj->getDocument()->Label.getValue())
_DocMap[App::quote(info.docLabel)] = obj->getDocument()->Label.getValue();
}
if(_Deps.insert(obj).second)
if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second)
_XLinks[obj->getFullName()] = std::move(info.xlink);
}
_XLinkRestores.reset();
@@ -4496,24 +4496,27 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) {
if(!owner || !owner->getNameInDocument())
return;
if(!clear || obj!=owner) {
if(!_Deps.erase(obj))
auto it = _Deps.find(obj);
if(it == _Deps.end())
return;
aboutToSetValue();
onBreakLink(obj);
if(obj->getDocument() == owner->getDocument())
obj->_removeBackLink(owner);
else
if (obj->getDocument() != owner->getDocument())
_XLinks.erase(obj->getFullName());
else if (!it->second)
obj->_removeBackLink(owner);
_Deps.erase(it);
hasSetValue();
return;
}
if(obj!=owner)
return;
for(auto obj : _Deps) {
for(auto &v : _Deps) {
auto obj = v.first;
if(!obj || !obj->getNameInDocument())
continue;
onBreakLink(obj);
if(obj->getDocument()==owner->getDocument())
if(!v.second && obj->getDocument()==owner->getDocument())
obj->_removeBackLink(owner);
}
_XLinks.clear();
@@ -4553,6 +4556,19 @@ void PropertyXLinkContainer::Save (Base::Writer &writer) const {
writer.Stream() << "\" docs=\"" << docSet.size();
}
std::ostringstream ss;
int hidden = 0;
int i=-1;
for(auto &v : _XLinks) {
++i;
if(v.second->getScope() == LinkScope::Hidden) {
ss << i << ' ';
++hidden;
}
}
if(hidden)
writer.Stream() << "\" hidden=\"" << ss.str();
writer.Stream() << "\">" << std::endl;
writer.incInd();
@@ -4575,6 +4591,15 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) {
auto count = reader.getAttributeAsUnsigned("count");
_XLinkRestores.reset(new std::vector<RestoreInfo>(count));
if(reader.hasAttribute("hidden")) {
std::istringstream iss(reader.getAttribute("hidden"));
int index;
while(iss >> index) {
if(index>=0 && index<static_cast<int>(count))
_XLinkRestores->at(index).hidden = true;
}
}
if(reader.hasAttribute("docs")) {
auto docCount = reader.getAttributeAsUnsigned("docs");
_DocMap.clear();
@@ -4593,6 +4618,8 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) {
for(auto &info : *_XLinkRestores) {
info.xlink.reset(createXLink());
if(info.hidden)
info.xlink->setScope(LinkScope::Hidden);
info.xlink->Restore(reader);
}
reader.readEndElement("XLinks");
@@ -4624,16 +4651,23 @@ bool PropertyXLinkContainer::isLinkedToDocument(const App::Document &doc) const
return false;
}
void PropertyXLinkContainer::updateDeps(std::set<DocumentObject*> &&newDeps) {
void PropertyXLinkContainer::updateDeps(std::map<DocumentObject*,bool> &&newDeps) {
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
if(!owner || !owner->getNameInDocument())
return;
newDeps.erase(owner);
for(auto obj : newDeps) {
for(auto &v : newDeps) {
auto obj = v.first;
if(obj && obj->getNameInDocument()) {
auto it = _Deps.find(obj);
if(it != _Deps.end()) {
if(v.second != it->second) {
if(v.second)
obj->_removeBackLink(owner);
else
obj->_addBackLink(owner);
}
_Deps.erase(it);
continue;
}
@@ -4643,21 +4677,21 @@ void PropertyXLinkContainer::updateDeps(std::set<DocumentObject*> &&newDeps) {
xlink.reset(createXLink());
xlink->setValue(obj);
}
xlink->setScope(v.second?LinkScope::Hidden:LinkScope::Global);
}
#ifndef USE_OLD_DAG
else
else if(!v.second)
obj->_addBackLink(owner);
#endif
onAddDep(obj);
}
}
for(auto obj : _Deps) {
for(auto &v : _Deps) {
auto obj = v.first;
if(!obj || !obj->getNameInDocument())
continue;
if(obj->getDocument()==owner->getDocument()) {
#ifndef USE_OLD_DAG
obj->_removeBackLink(owner);
#endif
if(!v.second)
obj->_removeBackLink(owner);
}else
_XLinks.erase(obj->getFullName());
onRemoveDep(obj);
@@ -4681,8 +4715,9 @@ void PropertyXLinkContainer::clearDeps() {
return;
#ifndef USE_OLD_DAG
if (!owner->testStatus(ObjectStatus::Destroy)) {
for(auto obj : _Deps) {
if(obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument())
for(auto &v : _Deps) {
auto obj = v.first;
if(!v.second && obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument())
obj->_removeBackLink(owner);
}
}
@@ -4692,8 +4727,11 @@ void PropertyXLinkContainer::clearDeps() {
_LinkRestored = false;
}
void PropertyXLinkContainer::getLinks(std::vector<App::DocumentObject *> &objs,
bool, std::vector<std::string> * /*subs*/, bool /*newStyle*/) const
void PropertyXLinkContainer::getLinks(std::vector<App::DocumentObject *> &objs,
bool all, std::vector<std::string> * /*subs*/, bool /*newStyle*/) const
{
objs.insert(objs.end(),_Deps.begin(),_Deps.end());
for(auto &v : _Deps) {
if(all || !v.second)
objs.push_back(v.first);
}
}

View File

@@ -1339,11 +1339,11 @@ protected:
virtual void onBreakLink(App::DocumentObject *obj);
virtual void onAddDep(App::DocumentObject *) {}
virtual void onRemoveDep(App::DocumentObject *) {}
void updateDeps(std::set<DocumentObject*> &&newDeps);
void updateDeps(std::map<DocumentObject*,bool> &&newDeps);
void clearDeps();
protected:
std::set<App::DocumentObject*> _Deps;
std::map<App::DocumentObject*,bool> _Deps;
std::map<std::string, std::unique_ptr<PropertyXLink> > _XLinks;
std::map<std::string, std::string> _DocMap;
bool _LinkRestored;
@@ -1353,6 +1353,7 @@ private:
std::unique_ptr<PropertyXLink> xlink;
std::string docName;
std::string docLabel;
bool hidden=false;
};
std::unique_ptr<std::vector<RestoreInfo> > _XLinkRestores;
};

View File

@@ -1023,38 +1023,39 @@ void PropertySheet::addDependencies(CellAddress key)
if (expression == 0)
return;
for(auto &dep : expression->getDeps()) {
for(auto &var : expression->getIdentifiers()) {
for(auto &dep : var.first.getDep(true)) {
App::DocumentObject *docObj = dep.first;
App::Document *doc = docObj->getDocument();
App::DocumentObject *docObj = dep.first;
App::Document *doc = docObj->getDocument();
std::string docObjName = docObj->getFullName();
std::string docObjName = docObj->getFullName();
owner->observeDocument(doc);
owner->observeDocument(doc);
documentObjectToCellMap[docObjName].insert(key);
cellToDocumentObjectMap[key].insert(docObjName);
++updateCount;
documentObjectToCellMap[docObjName].insert(key);
cellToDocumentObjectMap[key].insert(docObjName);
++updateCount;
for(auto &name : dep.second) {
std::string propName = docObjName + "." + name;
FC_LOG("dep " << key.toString() << " -> " << name);
for(auto &props : dep.second) {
std::string propName = docObjName + "." + props.first;
FC_LOG("dep " << key.toString() << " -> " << propName);
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
// Also an alias?
if (docObj==owner && name.size()) {
auto j = revAliasProp.find(name);
// Also an alias?
if (docObj==owner && props.first.size()) {
std::map<std::string, CellAddress>::const_iterator j = revAliasProp.find(props.first);
if (j != revAliasProp.end()) {
propName = docObjName + "." + j->second.toString();
FC_LOG("dep " << key.toString() << " -> " << propName);
if (j != revAliasProp.end()) {
propName = docObjName + "." + j->second.toString();
FC_LOG("dep " << key.toString() << " -> " << propName);
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
}
}
}
}
@@ -1123,6 +1124,16 @@ void PropertySheet::removeDependencies(CellAddress key)
void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const char *propName)
{
auto itD = _Deps.find(const_cast<App::DocumentObject*>(owner));
if(itD!=_Deps.end() && itD->second) {
// Check for hidden reference. Because a hidden reference is not
// protected by cyclic dependency checking, we need to take special
// care to prevent it from misbehave.
Sheet *sheet = Base::freecad_dynamic_cast<Sheet>(getContainer());
if(!sheet || sheet->testStatus(App::ObjectStatus::Recompute2))
return;
}
// First, search without actual property name for sub-object/link
// references, i.e indirect references. The dependencies of these
// references are too complex to track exactly, so we only track the
@@ -1135,7 +1146,7 @@ void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const
setDirty(cell);
}
if (propName) {
if (propName && *propName) {
// Now, we check for direct property references
it = propertyNameToCellMap.find(fullName + propName);
if (it != propertyNameToCellMap.end()) {
@@ -1302,7 +1313,7 @@ void PropertySheet::hasSetValue()
updateCount = 0;
std::set<App::DocumentObject*> deps;
std::map<App::DocumentObject*,bool> deps;
std::vector<std::string> labels;
unregisterElementReference();
UpdateElementReferenceExpressionVisitor<PropertySheet> v(*this);
@@ -1384,8 +1395,9 @@ bool PropertySheet::adjustLink(const std::set<DocumentObject*> &inList) {
continue;
try {
bool need_adjust = false;
for(auto docObj : expr->getDepObjects()) {
if (docObj && docObj != owner && inList.count(docObj)) {
for(auto &v : expr->getDepObjects()) {
auto docObj = v.first;
if (v.second && docObj && docObj!=owner && inList.count(docObj)) {
need_adjust = true;
break;
}