1
/***************************************************************************
2
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
#include <App/Application.h>
26
#include <App/Document.h>
27
#include <App/DocumentObject.h>
28
#include <Base/Reader.h>
29
#include <Base/Tools.h>
30
#include <Base/Writer.h>
31
#include <CXX/Objects.hxx>
33
#include "PropertyExpressionEngine.h"
34
#include "ExpressionVisitors.h"
37
FC_LOG_LEVEL_INIT("App", true);
42
namespace sp = std::placeholders;
44
TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyExpressionContainer , App::PropertyXLinkContainer)
46
static std::set<PropertyExpressionContainer*> _ExprContainers;
48
PropertyExpressionContainer::PropertyExpressionContainer() {
52
GetApplication().signalRelabelDocument.connect(PropertyExpressionContainer::slotRelabelDocument);
54
_ExprContainers.insert(this);
57
PropertyExpressionContainer::~PropertyExpressionContainer() {
58
_ExprContainers.erase(this);
61
void PropertyExpressionContainer::slotRelabelDocument(const App::Document &doc) {
62
// For use a private _ExprContainers to track all living
63
// PropertyExpressionContainer including those inside undo/redo stack,
64
// because document relabel is not undoable/redoable.
66
if(doc.getOldLabel() != doc.Label.getValue()) {
67
for(auto prop : _ExprContainers)
68
prop->onRelabeledDocument(doc);
72
///////////////////////////////////////////////////////////////////////////////////////
74
struct PropertyExpressionEngine::Private {
75
// For some reason, MSVC has trouble with vector of scoped_connection if
76
// defined in header, hence the private structure here.
77
std::vector<boost::signals2::scoped_connection> conns;
78
std::unordered_map<std::string, std::vector<ObjectIdentifier> > propMap;
81
///////////////////////////////////////////////////////////////////////////////////////
83
TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::PropertyExpressionContainer)
86
* @brief Construct a new PropertyExpressionEngine object.
89
PropertyExpressionEngine::PropertyExpressionEngine()
95
* @brief Destroy the PropertyExpressionEngine object.
98
PropertyExpressionEngine::~PropertyExpressionEngine() = default;
101
* @brief Estimate memory size of this property.
103
* \fixme Should probably return something else than 0.
105
* @return Size of object.
108
unsigned int PropertyExpressionEngine::getMemSize() const
113
Property *PropertyExpressionEngine::Copy() const
115
PropertyExpressionEngine * engine = new PropertyExpressionEngine();
117
for (const auto & it : expressions) {
119
if (it.second.expression)
120
info.expression = std::shared_ptr<Expression>(it.second.expression->copy());
121
engine->expressions[it.first] = info;
124
engine->validator = validator;
129
void PropertyExpressionEngine::hasSetValue()
131
App::DocumentObject *owner = dynamic_cast<App::DocumentObject*>(getContainer());
132
if(!owner || !owner->isAttachedToDocument() || owner->isRestoring() || testFlag(LinkDetached)) {
133
PropertyExpressionContainer::hasSetValue();
137
std::map<App::DocumentObject*,bool> deps;
138
std::vector<std::string> labels;
139
unregisterElementReference();
140
UpdateElementReferenceExpressionVisitor<PropertyExpressionEngine> v(*this);
141
for(auto &e : expressions) {
142
auto expr = e.second.expression;
144
expr->getDepObjects(deps,&labels);
149
registerLabelReferences(std::move(labels));
151
updateDeps(std::move(deps));
154
pimpl->conns.clear();
155
pimpl->propMap.clear();
157
// check if there is any hidden references
158
bool hasHidden = false;
159
for(auto &v : _Deps) {
167
pimpl = std::make_unique<Private>();
169
for(auto &e : expressions) {
170
auto expr = e.second.expression;
172
for(auto &dep : expr->getIdentifiers()) {
175
const ObjectIdentifier &var = dep.first;
176
for(auto &vdep : var.getDep(true)) {
177
auto obj = vdep.first;
178
auto objName = obj->getFullName() + ".";
179
for(auto &propName : vdep.second) {
180
std::string key = objName + propName;
181
auto &propDeps = pimpl->propMap[key];
182
if(propDeps.empty()) {
184
if(!propName.empty()) {
185
pimpl->conns.emplace_back(obj->signalChanged.connect(std::bind(
186
&PropertyExpressionEngine::slotChangedProperty,this,sp::_1,sp::_2)));
189
pimpl->conns.emplace_back(obj->signalChanged.connect(std::bind(
190
&PropertyExpressionEngine::slotChangedObject,this,sp::_1,sp::_2)));
194
propDeps.push_back(e.first);
201
PropertyExpressionContainer::hasSetValue();
204
void PropertyExpressionEngine::updateHiddenReference(const std::string &key) {
207
auto it = pimpl->propMap.find(key);
208
if(it == pimpl->propMap.end())
210
for(auto &var : it->second) {
211
auto it = expressions.find(var);
212
if(it == expressions.end() || it->second.busy)
214
Property *myProp = var.getProperty();
217
Base::StateLocker guard(it->second.busy);
220
value = it->second.expression->getValueAsAny();
221
if(!isAnyEqual(value, myProp->getPathValue(var)))
222
myProp->setPathValue(var, value);
223
}catch(Base::Exception &e) {
225
FC_ERR("Failed to evaluate property binding "
226
<< myProp->getFullName() << " on change of " << key);
227
}catch(std::bad_cast &) {
228
FC_ERR("Invalid type '" << value.type().name()
229
<< "' in property binding " << myProp->getFullName()
230
<< " on change of " << key);
231
}catch(std::exception &e) {
233
FC_ERR("Failed to evaluate property binding "
234
<< myProp->getFullName() << " on change of " << key);
239
void PropertyExpressionEngine::slotChangedObject(const App::DocumentObject &obj, const App::Property &) {
240
updateHiddenReference(obj.getFullName());
243
void PropertyExpressionEngine::slotChangedProperty(const App::DocumentObject &, const App::Property &prop) {
244
updateHiddenReference(prop.getFullName());
247
void PropertyExpressionEngine::Paste(const Property &from)
249
const PropertyExpressionEngine &fromee = dynamic_cast<const PropertyExpressionEngine&>(from);
251
AtomicPropertyChange signaller(*this);
254
for(auto &e : fromee.expressions) {
256
if (e.second.expression)
257
info.expression = std::shared_ptr<Expression>(e.second.expression->copy());
258
expressions[e.first] = info;
259
expressionChanged(e.first);
261
validator = fromee.validator;
262
signaller.tryInvoke();
265
void PropertyExpressionEngine::Save(Base::Writer &writer) const
267
writer.Stream() << writer.ind() << "<ExpressionEngine count=\"" << expressions.size();
268
if(PropertyExpressionContainer::_XLinks.empty()) {
269
writer.Stream() << "\">" << std::endl;
272
writer.Stream() << R"(" xlink="1">)" << std::endl;
274
PropertyExpressionContainer::Save(writer);
276
for (const auto & it : expressions) {
277
std::string expression, comment;
278
if (it.second.expression) {
279
expression = it.second.expression->toString(true);
280
comment = it.second.expression->comment;
283
writer.Stream() << writer.ind() << "<Expression path=\""
284
<< Property::encodeAttribute(it.first.toString()) <<"\" expression=\""
285
<< Property::encodeAttribute(expression) << "\"";
286
if (!comment.empty())
287
writer.Stream() << " comment=\""
288
<< Property::encodeAttribute(comment) << "\"";
289
writer.Stream() << "/>" << std::endl;
292
writer.Stream() << writer.ind() << "</ExpressionEngine>" << std::endl;
295
void PropertyExpressionEngine::Restore(Base::XMLReader &reader)
297
reader.readElement("ExpressionEngine");
298
int count = reader.getAttributeAsFloat("count");
300
if(reader.hasAttribute("xlink") && reader.getAttributeAsInteger("xlink"))
301
PropertyExpressionContainer::Restore(reader);
303
restoredExpressions = std::make_unique<std::vector<RestoredExpression>>();
304
restoredExpressions->reserve(count);
305
for (int i = 0; i < count; ++i) {
307
reader.readElement("Expression");
308
restoredExpressions->emplace_back();
309
auto &info = restoredExpressions->back();
310
info.path = reader.getAttribute("path");
311
info.expr = reader.getAttribute("expression");
312
if(reader.hasAttribute("comment"))
313
info.comment = reader.getAttribute("comment");
316
reader.readEndElement("ExpressionEngine");
320
* @brief Update graph structure with given path and expression.
322
* @param expression Expression to query for dependencies
323
* @param nodes Map with nodes of graph, including dependencies of 'expression'
324
* @param revNodes Reverse map of the nodes, containing only the given paths, without dependencies.
325
* @param edges Edges in graph
328
void PropertyExpressionEngine::buildGraphStructures(const ObjectIdentifier & path,
329
const std::shared_ptr<Expression> expression,
330
boost::unordered_map<ObjectIdentifier, int> & nodes,
331
boost::unordered_map<int, ObjectIdentifier> & revNodes,
332
std::vector<Edge> & edges) const
334
/* Insert target property into nodes structure */
335
if (nodes.find(path) == nodes.end()) {
336
int s = nodes.size();
342
revNodes[nodes[path]] = path;
345
/* Insert dependencies into nodes structure */
348
deps = expression->getDeps();
350
for(auto &dep : deps) {
351
for(auto &info : dep.second) {
352
if(info.first.empty())
354
for(auto &oid : info.second) {
355
ObjectIdentifier cPath(oid.canonicalPath());
356
if (nodes.find(cPath) == nodes.end()) {
357
int s = nodes.size();
360
edges.emplace_back(nodes[path], nodes[cPath]);
367
* @brief Create a canonical object identifier of the given object \a p.
368
* @param p ObjectIndentifier
369
* @return New ObjectIdentifier
372
ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier &p) const
374
DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
376
// Am I owned by a DocumentObject?
378
throw Base::RuntimeError("PropertyExpressionEngine must be owned by a DocumentObject.");
381
Property * prop = p.getProperty(&ptype);
383
// p pointing to a property...?
385
throw Base::RuntimeError(p.resolveErrorString().c_str());
387
if(ptype || prop->getContainer()!=getContainer())
390
// In case someone calls this with p pointing to a PropertyExpressionEngine for some reason
391
if (prop->isDerivedFrom(PropertyExpressionEngine::classTypeId))
394
// Dispatch call to actual canonicalPath implementation
395
return p.canonicalPath();
399
* @brief Number of expressions managed by this object.
400
* @return Number of expressions.
403
size_t PropertyExpressionEngine::numExpressions() const
405
return expressions.size();
408
void PropertyExpressionEngine::afterRestore()
410
DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
411
if(restoredExpressions && docObj) {
412
Base::FlagToggler<bool> flag(restoring);
413
AtomicPropertyChange signaller(*this);
415
PropertyExpressionContainer::afterRestore();
416
ObjectIdentifier::DocumentMapper mapper(this->_DocMap);
418
for(auto &info : *restoredExpressions) {
419
ObjectIdentifier path = ObjectIdentifier::parse(docObj, info.path);
420
if (!info.expr.empty()) {
421
std::shared_ptr<Expression> expression(Expression::parse(docObj, info.expr.c_str()));
423
expression->comment = std::move(info.comment);
424
setValue(path, expression);
427
signaller.tryInvoke();
429
restoredExpressions.reset();
432
void PropertyExpressionEngine::onContainerRestored() {
433
Base::FlagToggler<bool> flag(restoring);
434
unregisterElementReference();
435
UpdateElementReferenceExpressionVisitor<PropertyExpressionEngine> v(*this);
436
for(auto &e : expressions) {
437
auto expr = e.second.expression;
444
* @brief Get expression for \a path.
445
* @param path ObjectIndentifier to query for.
446
* @return Expression for \a path, or empty boost::any if not found.
449
const boost::any PropertyExpressionEngine::getPathValue(const App::ObjectIdentifier & path) const
451
// Get a canonical path
452
ObjectIdentifier usePath(canonicalPath(path));
454
ExpressionMap::const_iterator i = expressions.find(usePath);
456
if (i != expressions.end())
463
* @brief Set expression with optional comment for \a path.
464
* @param path Path to update
465
* @param expr New expression
466
* @param comment Optional comment.
469
void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, std::shared_ptr<Expression> expr)
471
ObjectIdentifier usePath(canonicalPath(path));
472
const Property * prop = usePath.getProperty();
474
// Try to access value; it should trigger an exception if it is not supported, or if the path is invalid
475
prop->getPathValue(usePath);
477
// Check if the current expression equals the new one and do nothing if so to reduce unneeded computations
478
ExpressionMap::iterator it = expressions.find(usePath);
479
if(it != expressions.end()
480
&& (expr == it->second.expression ||
481
(expr && it->second.expression
482
&& expr->isSame(*it->second.expression))))
488
std::string error = validateExpression(usePath, expr);
490
throw Base::RuntimeError(error.c_str());
491
AtomicPropertyChange signaller(*this);
492
expressions[usePath] = ExpressionInfo(expr);
493
expressionChanged(usePath);
494
signaller.tryInvoke();
495
} else if (it != expressions.end()) {
496
AtomicPropertyChange signaller(*this);
497
expressions.erase(it);
498
expressionChanged(usePath);
499
signaller.tryInvoke();
504
* @brief The cycle_detector struct is used by the boost graph routines to detect cycles in the graph.
507
struct cycle_detector : public boost::dfs_visitor<> {
508
cycle_detector( bool& has_cycle, int & src)
509
: _has_cycle(has_cycle), _src(src) { }
511
template <class Edge, class Graph>
512
void back_edge(Edge e, Graph&g) {
523
* @brief Build a graph of all expressions in \a exprs.
524
* @param exprs Expressions to use in graph
525
* @param revNodes Map from int[nodeid] to ObjectIndentifer.
526
* @param g Graph to update. May contain additional nodes than in revNodes, because of outside dependencies.
529
void PropertyExpressionEngine::buildGraph(const ExpressionMap & exprs,
530
boost::unordered_map<int, ObjectIdentifier> & revNodes,
531
DiGraph & g, ExecuteOption option) const
533
boost::unordered_map<ObjectIdentifier, int> nodes;
534
std::vector<Edge> edges;
536
// Build data structure for graph
537
for (const auto & expr : exprs) {
538
if(option!=ExecuteAll) {
539
auto prop = expr.first.getProperty();
541
throw Base::RuntimeError("Path does not resolve to a property.");
542
bool is_output = prop->testStatus(App::Property::Output)||(prop->getType()&App::Prop_Output);
543
if((is_output && option==ExecuteNonOutput) || (!is_output && option==ExecuteOutput))
545
if(option == ExecuteOnRestore
546
&& !prop->testStatus(Property::Transient)
547
&& !(prop->getType() & Prop_Transient)
548
&& !prop->testStatus(Property::EvalOnRestore))
551
buildGraphStructures(expr.first, expr.second.expression, nodes, revNodes, edges);
555
g = DiGraph(nodes.size());
557
// Add edges to graph
558
for (const auto & edge : edges)
559
add_edge(edge.first, edge.second, g);
562
bool has_cycle = false;
564
cycle_detector vis(has_cycle, src);
565
depth_first_search(g, visitor(vis));
568
std::string s = revNodes[src].toString() + " reference creates a cyclic dependency.";
570
throw Base::RuntimeError(s.c_str());
575
* The code below builds a graph for all expressions in the engine, and
576
* finds any circular dependencies. It also computes the internal evaluation
577
* order, in case properties depends on each other.
580
std::vector<App::ObjectIdentifier> PropertyExpressionEngine::computeEvaluationOrder(ExecuteOption option)
582
std::vector<App::ObjectIdentifier> evaluationOrder;
583
boost::unordered_map<int, ObjectIdentifier> revNodes;
586
buildGraph(expressions, revNodes, g, option);
588
/* Compute evaluation order for expressions */
590
topological_sort(g, std::back_inserter(c));
593
// we return the evaluation order for our properties, not the dependencies
594
// the topo sort will contain node ids for both our props and their deps
595
if (revNodes.find(i) != revNodes.end())
596
evaluationOrder.push_back(revNodes[i]);
599
return evaluationOrder;
603
* @brief Compute and update values of all registered expressions.
604
* @return StdReturn on success.
607
DocumentObjectExecReturn *App::PropertyExpressionEngine::execute(ExecuteOption option, bool *touched)
609
DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(getContainer());
612
throw Base::RuntimeError("PropertyExpressionEngine must be owned by a DocumentObject.");
615
return DocumentObject::StdReturn;
617
if(option == ExecuteOnRestore) {
619
for(auto &e : expressions) {
620
auto prop = e.first.getProperty();
623
if(prop->testStatus(App::Property::Transient)
624
|| (prop->getType()&App::Prop_Transient)
625
|| prop->testStatus(App::Property::EvalOnRestore))
632
return DocumentObject::StdReturn;
635
/* Resetter class, to ensure that the "running" variable gets set to false, even if
636
* an exception is thrown.
641
explicit resetter(bool & b) : _b(b) { _b = true; }
642
~resetter() { _b = false; }
650
// Compute evaluation order
651
std::vector<App::ObjectIdentifier> evaluationOrder = computeEvaluationOrder(option);
652
std::vector<ObjectIdentifier>::const_iterator it = evaluationOrder.begin();
654
#ifdef FC_PROPERTYEXPRESSIONENGINE_LOG
655
std::clog << "Computing expressions for " << getName() << std::endl;
658
/* Evaluate the expressions, and update properties */
659
for (;it != evaluationOrder.end();++it) {
661
// Get property to update
662
Property * prop = it->getProperty();
665
throw Base::RuntimeError("Path does not resolve to a property.");
667
DocumentObject* parent = freecad_dynamic_cast<DocumentObject>(prop->getContainer());
669
/* Make sure property belongs to the same container as this PropertyExpressionEngine */
670
if (parent != docObj)
671
throw Base::RuntimeError("Invalid property owner.");
673
/* Set value of property */
676
// Evaluate expression
677
std::shared_ptr<App::Expression> expression = expressions[*it].expression;
679
value = expression->getValueAsAny();
681
// Enable value comparison for all expression bindings to reduce
682
// unnecessary touch and recompute.
684
// This optimization is necessary for some hidden reference to
685
// work because it introduce dependency loop. The repeativtive
686
// recompute can be stopped if the expression evaluates the same
689
// In the future, we can generalize the optimization to all
690
// property modification, i.e. do not touch unless value change
692
// if (option == ExecuteOnRestore && prop->testStatus(Property::EvalOnRestore))
694
if (isAnyEqual(value, prop->getPathValue(*it)))
699
prop->setPathValue(*it, value);
701
}catch(Base::Exception &e) {
702
std::ostringstream ss;
703
ss << e.what() << std::endl << "in property binding '" << prop->getFullName() << "'";
704
e.setMessage(ss.str());
706
}catch(std::bad_cast &) {
707
std::ostringstream ss;
708
ss << "Invalid type '" << value.type().name() << "'";
709
ss << "\nin property binding '" << prop->getFullName() << "'";
710
throw Base::TypeError(ss.str().c_str());
711
}catch(std::exception &e) {
712
std::ostringstream ss;
713
ss << e.what() << "\nin property binding '" << prop->getFullName() << "'";
714
throw Base::RuntimeError(ss.str().c_str());
717
return DocumentObject::StdReturn;
721
* @brief Find paths to document object.
722
* @param obj Document object
723
* @param paths Object identifier
726
void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj,
727
std::vector<App::ObjectIdentifier> & paths) const
729
DocumentObject * owner = freecad_dynamic_cast<DocumentObject>(getContainer());
731
if (!owner || owner==obj)
734
for(auto &v : expressions) {
735
if (!v.second.expression)
737
const auto &deps = v.second.expression->getDeps();
738
auto it = deps.find(obj);
741
for(auto &dep : it->second)
742
paths.insert(paths.end(),dep.second.begin(),dep.second.end());
747
* @brief Determine whether any dependencies of any of the registered expressions have been touched.
748
* @return True if at least on dependency has been touched.
751
bool PropertyExpressionEngine::depsAreTouched() const
753
for(auto &v : _Deps) {
754
// v.second indicates if it is a hidden reference
755
if(!v.second && v.first->isTouched())
762
* @brief Validate the given path and expression.
763
* @param path Object Identifier for expression.
764
* @param expr Expression tree.
765
* @return Empty string on success, error message on failure.
768
std::string PropertyExpressionEngine::validateExpression(const ObjectIdentifier &path, std::shared_ptr<const Expression> expr) const
771
ObjectIdentifier usePath(canonicalPath(path));
774
error = validator(usePath, expr);
779
// Get document object
780
DocumentObject * pathDocObj = usePath.getDocumentObject();
783
auto inList = pathDocObj->getInListEx(true);
784
for(auto &v : expr->getDepObjects()) {
785
auto docObj = v.first;
786
if(!v.second && inList.count(docObj)) {
787
std::stringstream ss;
788
ss << "cyclic reference to " << docObj->getFullName();
793
// Check for internal document object dependencies
795
// Copy current expressions
796
ExpressionMap newExpressions = expressions;
798
// Add expression in question
799
std::shared_ptr<Expression> exprClone(expr->copy());
800
newExpressions[usePath].expression = exprClone;
802
// Build graph; an exception will be thrown if it is not a DAG
804
boost::unordered_map<int, ObjectIdentifier> revNodes;
807
buildGraph(newExpressions, revNodes, g);
809
catch (const Base::Exception & e) {
817
* @brief Rename paths based on \a paths.
818
* @param paths Map with current and new object identifier.
821
void PropertyExpressionEngine::renameExpressions(const std::map<ObjectIdentifier, ObjectIdentifier> & paths)
823
ExpressionMap newExpressions;
824
std::map<ObjectIdentifier, ObjectIdentifier> canonicalPaths;
826
/* ensure input map uses canonical paths */
827
for (const auto & path : paths)
828
canonicalPaths[canonicalPath(path.first)] = path.second;
830
for (ExpressionMap::const_iterator i = expressions.begin(); i != expressions.end(); ++i) {
831
std::map<ObjectIdentifier, ObjectIdentifier>::const_iterator j = canonicalPaths.find(i->first);
834
if (j != canonicalPaths.end())
835
newExpressions[j->second] = i->second;
837
newExpressions[i->first] = i->second;
841
expressions = newExpressions;
842
for (ExpressionMap::const_iterator i = expressions.begin(); i != expressions.end(); ++i)
843
expressionChanged(i->first);
849
* @brief Rename object identifiers in the registered expressions.
850
* @param paths Map with current and new object identifiers.
853
void PropertyExpressionEngine::renameObjectIdentifiers(const std::map<ObjectIdentifier, ObjectIdentifier> &paths)
855
for (const auto & it : expressions) {
856
RenameObjectIdentifierExpressionVisitor<PropertyExpressionEngine> v(*this, paths, it.first);
857
it.second.expression->visit(v);
861
PyObject *PropertyExpressionEngine::getPyObject()
864
for (const auto & it : expressions) {
866
tuple.setItem(0, Py::String(it.first.toString()));
867
auto expr = it.second.expression;
868
tuple.setItem(1, expr ? Py::String(expr->toString()) : Py::None());
871
return Py::new_reference_to(list);
874
void PropertyExpressionEngine::setPyObject(PyObject *)
876
throw Base::RuntimeError("Property is read-only");
879
/* The policy implemented in the following function is to auto erase binding in
880
* case linked object is gone. I think it is better to cause error and get
883
void PropertyExpressionEngine::breakLink(App::DocumentObject *obj, bool clear) {
884
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
887
if(_Deps.count(obj)==0 && (!clear || obj!=owner || _Deps.empty()))
889
AtomicPropertyChange signaler(*this);
890
for(auto it=expressions.begin(),itNext=it;it!=expressions.end();it=itNext) {
892
const auto &deps = it->second.expression->getDepObjects();
894
// here means we are breaking all expression, except those that has
895
// no depdenecy or self dependency
896
if(deps.empty() || (deps.size()==1 && *deps.begin()==owner))
898
}else if(!deps.count(obj))
900
auto path = it->first;
901
expressions.erase(it);
902
expressionChanged(path);
907
bool PropertyExpressionEngine::adjustLink(const std::set<DocumentObject*> &inList) {
908
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
912
for(auto &v : _Deps) {
913
if(inList.count(v.first)) {
921
AtomicPropertyChange signaler(*this);
922
for(auto &v : expressions) {
924
if(v.second.expression && v.second.expression->adjustLinks(inList))
925
expressionChanged(v.first);
926
}catch(Base::Exception &e) {
927
std::ostringstream ss;
928
ss << "Failed to adjust link for " << owner->getFullName() << " in expression "
929
<< v.second.expression->toString() << ": " << e.what();
930
throw Base::RuntimeError(ss.str());
936
void PropertyExpressionEngine::updateElementReference(DocumentObject *feature, bool reverse, bool notify)
940
unregisterElementReference();
941
UpdateElementReferenceExpressionVisitor<PropertyExpressionEngine> v(*this,feature,reverse);
942
for(auto &e : expressions) {
943
if (e.second.expression) {
944
e.second.expression->visit(v);
946
expressionChanged(e.first);
951
if(feature && v.changed()) {
952
auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
954
owner->onUpdateElementReference(this);
958
bool PropertyExpressionEngine::referenceChanged() const {
962
Property *PropertyExpressionEngine::CopyOnImportExternal(
963
const std::map<std::string,std::string> &nameMap) const
965
std::unique_ptr<PropertyExpressionEngine> engine;
966
for(auto it=expressions.begin();it!=expressions.end();++it) {
967
#ifdef BOOST_NO_CXX11_SMART_PTR
968
std::shared_ptr<Expression> expr(it->second.expression->importSubNames(nameMap).release());
970
std::shared_ptr<Expression> expr(it->second.expression->importSubNames(nameMap));
975
engine = std::make_unique<PropertyExpressionEngine>();
976
for(auto it2=expressions.begin();it2!=it;++it2) {
977
engine->expressions[it2->first] = ExpressionInfo(
978
std::shared_ptr<Expression>(it2->second.expression->copy()));
981
expr = it->second.expression;
982
engine->expressions[it->first] = ExpressionInfo(expr);
986
engine->validator = validator;
987
return engine.release();
990
Property *PropertyExpressionEngine::CopyOnLabelChange(App::DocumentObject *obj,
991
const std::string &ref, const char *newLabel) const
993
std::unique_ptr<PropertyExpressionEngine> engine;
994
for(auto it=expressions.begin();it!=expressions.end();++it) {
995
#ifdef BOOST_NO_CXX11_SMART_PTR
996
std::shared_ptr<Expression> expr(it->second.expression->updateLabelReference(obj,ref,newLabel).release());
998
std::shared_ptr<Expression> expr(it->second.expression->updateLabelReference(obj,ref,newLabel));
1000
if(!expr && !engine)
1003
engine = std::make_unique<PropertyExpressionEngine>();
1004
for(auto it2=expressions.begin();it2!=it;++it2) {
1005
ExpressionInfo info;
1006
if (it2->second.expression)
1007
info.expression = std::shared_ptr<Expression>(it2->second.expression->copy());
1008
engine->expressions[it2->first] = info;
1011
expr = it->second.expression;
1012
engine->expressions[it->first] = ExpressionInfo(expr);
1016
engine->validator = validator;
1017
return engine.release();
1020
Property *PropertyExpressionEngine::CopyOnLinkReplace(const App::DocumentObject *parent,
1021
App::DocumentObject *oldObj, App::DocumentObject *newObj) const
1023
std::unique_ptr<PropertyExpressionEngine> engine;
1024
for(auto it=expressions.begin();it!=expressions.end();++it) {
1025
#ifdef BOOST_NO_CXX11_SMART_PTR
1026
std::shared_ptr<Expression> expr(
1027
it->second.expression->replaceObject(parent,oldObj,newObj).release());
1029
std::shared_ptr<Expression> expr(
1030
it->second.expression->replaceObject(parent,oldObj,newObj));
1032
if(!expr && !engine)
1035
engine = std::make_unique<PropertyExpressionEngine>();
1036
for(auto it2=expressions.begin();it2!=it;++it2) {
1037
ExpressionInfo info;
1038
if (it2->second.expression)
1039
info.expression = std::shared_ptr<Expression>(it2->second.expression->copy());
1040
engine->expressions[it2->first] = info;
1043
expr = it->second.expression;
1044
engine->expressions[it->first] = ExpressionInfo(expr);
1048
engine->validator = validator;
1049
return engine.release();
1052
std::map<App::ObjectIdentifier, const App::Expression*>
1053
PropertyExpressionEngine::getExpressions() const
1055
std::map<App::ObjectIdentifier, const Expression*> res;
1056
for(auto &v : expressions)
1057
res[v.first] = v.second.expression.get();
1061
void PropertyExpressionEngine::setExpressions(
1062
std::map<App::ObjectIdentifier, App::ExpressionPtr> &&exprs)
1064
AtomicPropertyChange signaller(*this);
1065
#ifdef BOOST_NO_CXX11_SMART_PTR
1066
for(auto &v : exprs)
1067
setValue(v.first,std::shared_ptr<Expression>(v.second.release()));
1069
for(auto &v : exprs)
1070
setValue(v.first,std::move(v.second));
1074
void PropertyExpressionEngine::onRelabeledDocument(const App::Document &doc)
1076
RelabelDocumentExpressionVisitor v(doc);
1077
for(auto &e : expressions) {
1078
if (e.second.expression)
1079
e.second.expression->visit(v);