PropertyExpressionEngine: convert to link type property

PropertyExpressionEngine is changed to derived from a new class
PropertyExpressionContainer, which is in turn derives from
PropertyXLinkContainer. This makes PropertyExpressionEngine a link type
property that is capable of external linking. It now uses the unified
link property APIs for dependency management and tracking of object
life time, re-labeling, etc.

ObjectIdentifier is modified to support sub-object reference, but is
not exposed to end-user, because expression syntax is kept mostly
unchanged, which will be submitted in future PR. There is, however,
one small change in expression syntax (ExpressionParser.y) to introduce
local property reference to avoid ambiguity mentioned in
FreeCAD/FreeCAD#1619

Modified Expression/ExpressionModifier interface to support various link
property API for link modification.
This commit is contained in:
Zheng, Lei
2019-06-29 17:30:51 +08:00
committed by wmayer
parent 29bc24a5cf
commit ced27a69c6
25 changed files with 3895 additions and 1645 deletions

View File

@@ -28,7 +28,7 @@
#include <boost/signals2.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <App/Property.h>
#include <App/PropertyLinks.h>
#include <App/Expression.h>
#include <set>
@@ -44,11 +44,38 @@ class DocumentObjectExecReturn;
class ObjectIdentifier;
class Expression;
class AppExport PropertyExpressionEngine : public App::Property, private App::AtomicPropertyChangeInterface<PropertyExpressionEngine>
class AppExport PropertyExpressionContainer : public App::PropertyXLinkContainer
{
TYPESYSTEM_HEADER();
public:
PropertyExpressionContainer();
virtual ~PropertyExpressionContainer();
virtual std::map<App::ObjectIdentifier, const App::Expression*> getExpressions() const = 0;
virtual void setExpressions(std::map<App::ObjectIdentifier, App::ExpressionPtr> &&exprs) = 0;
protected:
virtual void onRelabeledDocument(const App::Document &doc) = 0;
private:
static void slotRelabelDocument(const App::Document &doc);
};
class AppExport PropertyExpressionEngine : public App::PropertyExpressionContainer,
private App::AtomicPropertyChangeInterface<PropertyExpressionEngine>
{
TYPESYSTEM_HEADER();
public:
virtual void updateElementReference(
App::DocumentObject *feature, bool reverse=false, bool notify=false) override;
virtual bool referenceChanged() const override;
virtual bool adjustLink(const std::set<App::DocumentObject *> &inList) override;
virtual Property *CopyOnImportExternal(const std::map<std::string,std::string> &nameMap) const override;
virtual Property *CopyOnLabelChange(App::DocumentObject *obj,
const std::string &ref, const char *newLabel) const override;
virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent,
App::DocumentObject *oldObj, App::DocumentObject *newObj) const override;
typedef boost::function<std::string (const App::ObjectIdentifier & path, boost::shared_ptr<const App::Expression> expr)> ValidatorFunc;
@@ -58,22 +85,17 @@ public:
struct ExpressionInfo {
boost::shared_ptr<App::Expression> expression; /**< The actual expression tree */
std::string comment; /**< Optional comment for this expression */
ExpressionInfo(boost::shared_ptr<App::Expression> expression = boost::shared_ptr<App::Expression>(), const char * comment = 0) {
ExpressionInfo(boost::shared_ptr<App::Expression> expression = boost::shared_ptr<App::Expression>()) {
this->expression = expression;
if (comment)
this->comment = comment;
}
ExpressionInfo(const ExpressionInfo & other) {
expression = other.expression;
comment = other.comment;
}
ExpressionInfo & operator=(const ExpressionInfo & other) {
expression = other.expression;
comment = other.comment;
return *this;
}
};
@@ -83,6 +105,10 @@ public:
unsigned int getMemSize (void) const;
virtual std::map<App::ObjectIdentifier, const App::Expression*> getExpressions() const override;
virtual void setExpressions(std::map<App::ObjectIdentifier, App::ExpressionPtr> &&exprs) override;
virtual void onRelabeledDocument(const App::Document &doc) override;
void setValue() { } // Dummy
Property *Copy(void) const;
@@ -93,20 +119,31 @@ public:
void Restore(Base::XMLReader &reader);
void setValue(const App::ObjectIdentifier &path, boost::shared_ptr<App::Expression> expr, const char * comment = 0);
void setValue(const App::ObjectIdentifier &path, boost::shared_ptr<App::Expression> expr);
const boost::any getPathValue(const App::ObjectIdentifier & path) const;
DocumentObjectExecReturn * execute();
void getDocumentObjectDeps(std::vector<DocumentObject*> & docObjs) const;
/// Execute options
enum ExecuteOption {
/// Execute all expression
ExecuteAll,
/// Execute only output property bindings
ExecuteOutput,
/// Execute only non-output property bindings
ExecuteNonOutput,
/// Execute on document restore
ExecuteOnRestore,
};
/** Evaluate the expressions
*
* @param option: execution option, see ExecuteOption.
*/
DocumentObjectExecReturn * execute(ExecuteOption option=ExecuteAll, bool *touched=0);
void getPathsToDocumentObject(DocumentObject*, std::vector<App::ObjectIdentifier> & paths) const;
bool depsAreTouched() const;
boost::unordered_map<const App::ObjectIdentifier, const ExpressionInfo> getExpressions() const;
/* Expression validator */
void setValidator(ValidatorFunc f) { validator = f; }
@@ -116,45 +153,53 @@ public:
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths);
const App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier &p) const;
App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier &p) const;
size_t numExpressions() const;
void slotObjectRenamed(const App::DocumentObject & obj);
void slotObjectDeleted(const DocumentObject &obj);
///signal called when an expression was changed
boost::signals2::signal<void (const App::ObjectIdentifier &)> expressionChanged;
void onDocumentRestored();
virtual void afterRestore() override;
virtual void onContainerRestored() override;
/* Python interface */
PyObject *getPyObject(void);
void setPyObject(PyObject *);
protected:
virtual void hasSetValue() override;
private:
typedef boost::adjacency_list< boost::listS, boost::vecS, boost::directedS > DiGraph;
typedef std::pair<int, int> Edge;
typedef boost::unordered_map<const App::ObjectIdentifier, ExpressionInfo> ExpressionMap;
std::vector<App::ObjectIdentifier> computeEvaluationOrder();
std::vector<App::ObjectIdentifier> computeEvaluationOrder(ExecuteOption option);
void buildGraphStructures(const App::ObjectIdentifier &path,
const boost::shared_ptr<Expression> expression, boost::unordered_map<App::ObjectIdentifier, int> &nodes,
boost::unordered_map<int, App::ObjectIdentifier> &revNodes, std::vector<Edge> &edges) const;
void buildGraph(const ExpressionMap &exprs,
boost::unordered_map<int, App::ObjectIdentifier> &revNodes, DiGraph &g) const;
boost::unordered_map<int, App::ObjectIdentifier> &revNodes,
DiGraph &g, ExecuteOption option=ExecuteAll) const;
bool running; /**< Boolean used to avoid loops */
bool restoring = false;
ExpressionMap expressions; /**< Stored expressions */
ValidatorFunc validator; /**< Valdiator functor */
ExpressionMap restoredExpressions; /**< Expressions are read from file to this map first before they are validated and inserted into the actual map */
struct RestoredExpression {
std::string path;
std::string expr;
std::string comment;
};
/**< 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;
friend class AtomicPropertyChange;