Skip to content

Commit

Permalink
PropertyExpressionEngine: convert to link type property
Browse files Browse the repository at this point in the history
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
#1619

Modified Expression/ExpressionModifier interface to support various link
property API for link modification.
  • Loading branch information
realthunder committed Jul 14, 2019
1 parent ccda5af commit 0b11871
Show file tree
Hide file tree
Showing 25 changed files with 3,865 additions and 1,615 deletions.
1 change: 1 addition & 0 deletions src/App/Application.cpp
Expand Up @@ -1671,6 +1671,7 @@ void Application::initTypes(void)
App ::FunctionExpression ::init();
App ::BooleanExpression ::init();
App ::RangeExpression ::init();
App ::PyObjectExpression ::init();

// register transaction type
new App::TransactionProducer<TransactionDocumentObject>
Expand Down
18 changes: 9 additions & 9 deletions src/App/Document.cpp
Expand Up @@ -363,7 +363,7 @@ void Document::exportGraphviz(std::ostream& out) const

void addExpressionSubgraphIfNeeded(DocumentObject * obj, bool CSsubgraphs) {

boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo> expressions = obj->ExpressionEngine.getExpressions();
auto expressions = obj->ExpressionEngine.getExpressions();

if (!expressions.empty()) {

Expand All @@ -385,11 +385,11 @@ void Document::exportGraphviz(std::ostream& out) const
}

// Create subgraphs for all documentobjects that it depends on; it will depend on some property there
boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo>::const_iterator i = expressions.begin();
auto i = expressions.begin();
while (i != expressions.end()) {
std::set<ObjectIdentifier> deps;

i->second.expression->getDeps(deps);
i->second->getIdentifiers(deps);

std::set<ObjectIdentifier>::const_iterator j = deps.begin();
while (j != deps.end()) {
Expand Down Expand Up @@ -477,8 +477,8 @@ void Document::exportGraphviz(std::ostream& out) const
}

// Add expressions and its dependencies
boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo> expressions = docObj->ExpressionEngine.getExpressions();
boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo>::const_iterator i = expressions.begin();
auto expressions = docObj->ExpressionEngine.getExpressions();
auto i = expressions.begin();

// Add nodes for each property that has an expression attached to it
while (i != expressions.end()) {
Expand All @@ -498,7 +498,7 @@ void Document::exportGraphviz(std::ostream& out) const

// Get dependencies
std::set<ObjectIdentifier> deps;
i->second.expression->getDeps(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();
Expand Down Expand Up @@ -639,12 +639,12 @@ void Document::exportGraphviz(std::ostream& out) const
const DocumentObject * docObj = *j;

// Add expressions and its dependencies
boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo> expressions = docObj->ExpressionEngine.getExpressions();
boost::unordered_map<const App::ObjectIdentifier, const PropertyExpressionEngine::ExpressionInfo>::const_iterator i = expressions.begin();
auto expressions = docObj->ExpressionEngine.getExpressions();
auto i = expressions.begin();

while (i != expressions.end()) {
std::set<ObjectIdentifier> deps;
i->second.expression->getDeps(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();
Expand Down
46 changes: 2 additions & 44 deletions src/App/DocumentObject.cpp
Expand Up @@ -740,13 +740,11 @@ void DocumentObject::Save (Base::Writer &writer) const
* @brief Associate the expression \expr with the object identifier \a path in this document object.
* @param path Target object identifier for the result of the expression
* @param expr Expression tree
* @param comment Optional comment describing the expression
*/

void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr<Expression> expr, const char * comment)
void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr<Expression> expr)
{
ExpressionEngine.setValue(path, expr, comment);
connectRelabelSignals();
ExpressionEngine.setValue(path, expr);
}

/**
Expand Down Expand Up @@ -777,46 +775,6 @@ void DocumentObject::renameObjectIdentifiers(const std::map<ObjectIdentifier, Ob
ExpressionEngine.renameObjectIdentifiers(paths);
}

/**
* @brief Helper function that sets up a signal to track document object renames.
*/

void DocumentObject::connectRelabelSignals()
{
// Only keep signal if the ExpressionEngine has at least one expression
if (ExpressionEngine.numExpressions() > 0) {

// Not already connected?
if (!onRelabledObjectConnection.connected()) {
onRelabledObjectConnection = getDocument()->signalRelabelObject
.connect(boost::bind(&PropertyExpressionEngine::slotObjectRenamed,
&ExpressionEngine, _1));
}

// Connect to signalDeletedObject, to properly track deletion of other objects
// that might be referenced in an expression
if (!onDeletedObjectConnection.connected()) {
onDeletedObjectConnection = getDocument()->signalDeletedObject
.connect(boost::bind(&PropertyExpressionEngine::slotObjectDeleted,
&ExpressionEngine, _1));
}

try {
// Crude method to resolve all expression dependencies
ExpressionEngine.execute();
}
catch (...) {
// Ignore any error
}
}
else {
// Disconnect signals; nothing to track now
onRelabledObjectConnection.disconnect();
onRelabledDocumentConnection.disconnect();
onDeletedObjectConnection.disconnect();
}
}

void DocumentObject::onDocumentRestored()
{
//call all extensions
Expand Down
9 changes: 1 addition & 8 deletions src/App/DocumentObject.h
Expand Up @@ -348,14 +348,12 @@ class AppExport DocumentObject: public App::TransactionalObject

/* Expression support */

virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr<App::Expression> expr, const char *comment = 0);
virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr<App::Expression> expr);

virtual const PropertyExpressionEngine::ExpressionInfo getExpression(const ObjectIdentifier &path) const;

virtual void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths);

virtual void connectRelabelSignals();

const std::string & getOldLabel() const { return oldLabel; }

const char *getViewProviderNameStored() const {
Expand Down Expand Up @@ -528,11 +526,6 @@ class AppExport DocumentObject: public App::TransactionalObject
/// pointer to the document this object belongs to
App::Document* _pDoc;

// Connections to track relabeling of document and document objects
boost::signals2::scoped_connection onRelabledDocumentConnection;
boost::signals2::scoped_connection onRelabledObjectConnection;
boost::signals2::scoped_connection onDeletedObjectConnection;

/// Old label; used for renaming expressions
std::string oldLabel;

Expand Down
13 changes: 13 additions & 0 deletions src/App/DocumentObjectPy.xml
Expand Up @@ -167,6 +167,19 @@ non-object sub-element name if any.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="resolveSubElement" Const="true">
<Documentation>
<UserDocu>
resolveSubElement(subname,append,type) -- resolve both new and old style sub element

subname: subname reference contianing object hierarchy
append: Whether to append object hierarchy prefix inside subname to returned element name
type: 0: normal, 1: for import, 2: for export

Return tuple(obj,newElementName,oldElementName)
</UserDocu>
</Documentation>
</Methode>
<Methode Name="adjustRelativeLinks">
<Documentation>
<UserDocu>adjustRelativeLinks(parent,recursive=True) -- auto correct potential cyclic dependencies</UserDocu>
Expand Down
33 changes: 30 additions & 3 deletions src/App/DocumentObjectPyImp.cpp
Expand Up @@ -27,6 +27,7 @@
#include "DocumentObject.h"
#include "Document.h"
#include "Expression.h"
#include "GeoFeature.h"
#include "GroupExtension.h"
#include "GeoFeatureGroupExtension.h"

Expand Down Expand Up @@ -327,9 +328,11 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
else if (PyString_Check(expr)) {
const char * exprStr = PyString_AsString(expr);
#endif
boost::shared_ptr<Expression> shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr));
boost::shared_ptr<Expression> shared_expr(Expression::parse(getDocumentObjectPtr(), exprStr));
if(shared_expr && comment)
shared_expr->comment = comment;

getDocumentObjectPtr()->setExpression(p, shared_expr, comment);
getDocumentObjectPtr()->setExpression(p, shared_expr);
}
else if (PyUnicode_Check(expr)) {
#if PY_MAJOR_VERSION >= 3
Expand All @@ -341,7 +344,9 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args)
Py_DECREF(unicode);
boost::shared_ptr<Expression> shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr.c_str()));

getDocumentObjectPtr()->setExpression(p, shared_expr, comment);
if(shared_expr && comment)
shared_expr->comment = comment;
getDocumentObjectPtr()->setExpression(p, shared_expr);
}
else {
// utf-8 encoding failed
Expand Down Expand Up @@ -773,6 +778,28 @@ PyObject *DocumentObjectPy::resolve(PyObject *args)
Py_Return;
}

PyObject *DocumentObjectPy::resolveSubElement(PyObject *args)
{
const char *subname;
PyObject *append = Py_False;
int type = 0;
if (!PyArg_ParseTuple(args, "s|Oi",&subname,&append,&type))
return NULL; // NULL triggers exception

PY_TRY {
std::pair<std::string,std::string> elementName;
auto obj = GeoFeature::resolveElement(getDocumentObjectPtr(), subname,elementName,
PyObject_IsTrue(append),(GeoFeature::ElementNameType)type);
Py::Tuple ret(3);
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
ret.setItem(1,Py::String(elementName.first));
ret.setItem(2,Py::String(elementName.second));
return Py::new_reference_to(ret);
} PY_CATCH;

Py_Return;
}

Py::List DocumentObjectPy::getParents() const {
Py::List ret;
for(auto &v : getDocumentObjectPtr()->getParents())
Expand Down

0 comments on commit 0b11871

Please sign in to comment.