From 0b11871ce5a00c512043b55b59a725228275d548 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sat, 29 Jun 2019 17:30:51 +0800 Subject: [PATCH] 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. --- src/App/Application.cpp | 1 + src/App/Document.cpp | 18 +- src/App/DocumentObject.cpp | 46 +- src/App/DocumentObject.h | 9 +- src/App/DocumentObjectPy.xml | 13 + src/App/DocumentObjectPyImp.cpp | 33 +- src/App/Expression.cpp | 1265 +++++++++++-- src/App/Expression.h | 242 ++- src/App/ExpressionParser.tab.c | 492 ++--- src/App/ExpressionParser.y | 56 +- src/App/ExpressionVisitors.h | 94 +- src/App/ObjectIdentifier.cpp | 1577 ++++++++++++----- src/App/ObjectIdentifier.h | 292 ++- src/App/Property.h | 9 + src/App/PropertyExpressionEngine.cpp | 760 ++++---- src/App/PropertyExpressionEngine.h | 91 +- src/App/PropertyGeo.cpp | 149 +- src/App/PropertyGeo.h | 25 +- src/App/PropertyUnits.cpp | 15 +- src/App/Range.cpp | 52 +- src/App/Range.h | 24 +- .../Sketcher/App/PropertyConstraintList.cpp | 174 +- src/Mod/Sketcher/App/PropertyConstraintList.h | 13 +- src/Mod/Sketcher/App/SketchObject.cpp | 28 +- src/Mod/Sketcher/App/SketchObject.h | 2 +- 25 files changed, 3865 insertions(+), 1615 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 00488e15610d..439e3b53ef5e 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -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 diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 08cd71fb964f..5e196981e732 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -363,7 +363,7 @@ void Document::exportGraphviz(std::ostream& out) const void addExpressionSubgraphIfNeeded(DocumentObject * obj, bool CSsubgraphs) { - boost::unordered_map expressions = obj->ExpressionEngine.getExpressions(); + auto expressions = obj->ExpressionEngine.getExpressions(); if (!expressions.empty()) { @@ -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_iterator i = expressions.begin(); + auto i = expressions.begin(); while (i != expressions.end()) { std::set deps; - i->second.expression->getDeps(deps); + i->second->getIdentifiers(deps); std::set::const_iterator j = deps.begin(); while (j != deps.end()) { @@ -477,8 +477,8 @@ void Document::exportGraphviz(std::ostream& out) const } // Add expressions and its dependencies - boost::unordered_map expressions = docObj->ExpressionEngine.getExpressions(); - boost::unordered_map::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()) { @@ -498,7 +498,7 @@ void Document::exportGraphviz(std::ostream& out) const // Get dependencies std::set 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::const_iterator j = deps.begin(); @@ -639,12 +639,12 @@ void Document::exportGraphviz(std::ostream& out) const const DocumentObject * docObj = *j; // Add expressions and its dependencies - boost::unordered_map expressions = docObj->ExpressionEngine.getExpressions(); - boost::unordered_map::const_iterator i = expressions.begin(); + auto expressions = docObj->ExpressionEngine.getExpressions(); + auto i = expressions.begin(); while (i != expressions.end()) { std::set 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::const_iterator k = deps.begin(); diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index 9cb49a06132f..0819bd937a8c 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -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 expr, const char * comment) +void DocumentObject::setExpression(const ObjectIdentifier &path, boost::shared_ptr expr) { - ExpressionEngine.setValue(path, expr, comment); - connectRelabelSignals(); + ExpressionEngine.setValue(path, expr); } /** @@ -777,46 +775,6 @@ void DocumentObject::renameObjectIdentifiers(const std::map 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 diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index ad00d506baf7..23da6b1270f8 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -348,14 +348,12 @@ class AppExport DocumentObject: public App::TransactionalObject /* Expression support */ - virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr expr, const char *comment = 0); + virtual void setExpression(const ObjectIdentifier & path, boost::shared_ptr expr); virtual const PropertyExpressionEngine::ExpressionInfo getExpression(const ObjectIdentifier &path) const; virtual void renameObjectIdentifiers(const std::map & paths); - virtual void connectRelabelSignals(); - const std::string & getOldLabel() const { return oldLabel; } const char *getViewProviderNameStored() const { @@ -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; diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index b57c0dec5daa..6a13d9bade80 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -167,6 +167,19 @@ non-object sub-element name if any. + + + +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) + + + adjustRelativeLinks(parent,recursive=True) -- auto correct potential cyclic dependencies diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index ea1552f1f98d..202058b35396 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -27,6 +27,7 @@ #include "DocumentObject.h" #include "Document.h" #include "Expression.h" +#include "GeoFeature.h" #include "GroupExtension.h" #include "GeoFeatureGroupExtension.h" @@ -327,9 +328,11 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args) else if (PyString_Check(expr)) { const char * exprStr = PyString_AsString(expr); #endif - boost::shared_ptr shared_expr(ExpressionParser::parse(getDocumentObjectPtr(), exprStr)); + boost::shared_ptr 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 @@ -341,7 +344,9 @@ PyObject* DocumentObjectPy::setExpression(PyObject * args) Py_DECREF(unicode); boost::shared_ptr 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 @@ -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 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()) diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index cfddd7e64311..c35b068bbef0 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -30,6 +30,7 @@ # pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor" #endif +#include #include "Base/Exception.h" #include #include @@ -53,6 +54,11 @@ #include #include +using namespace Base; +using namespace App; + +FC_LOG_LEVEL_INIT("Expression",true,true) + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -72,8 +78,86 @@ #pragma warning(disable : 4065) #endif -using namespace Base; -using namespace App; +#define __EXPR_THROW(_e,_msg,_expr) do {\ + std::ostringstream ss;\ + ss << _msg;\ + if(_expr) ss << std::endl << (_expr)->toString();\ + throw _e(ss.str().c_str());\ +}while(0) + +#define _EXPR_THROW(_msg,_expr) __EXPR_THROW(ExpressionError,_msg,_expr) + +#define __EXPR_SET_MSG(_e,_msg,_expr) do {\ + std::ostringstream ss;\ + ss << _msg << _e.what();\ + if(_expr) ss << std::endl << (_expr)->toString();\ + _e.setMessage(ss.str());\ +}while(0) + +#define _EXPR_RETHROW(_e,_msg,_expr) do {\ + __EXPR_SET_MSG(_e,_msg,_expr);\ + throw;\ +}while(0) + +#define _EXPR_PY_THROW(_msg,_expr) do {\ + Base::PyException _e;\ + __EXPR_SET_MSG(_e,_msg,_expr);\ + _e.raiseException();\ +}while(0) + +#define EXPR_PY_THROW(_expr) _EXPR_PY_THROW("",_expr) + +#define EXPR_THROW(_msg) _EXPR_THROW(_msg,this) + +#define RUNTIME_THROW(_msg) __EXPR_THROW(Base::RuntimeError,_msg, (Expression*)0) + +#define TYPE_THROW(_msg) __EXPR_THROW(Base::TypeError,_msg, (Expression*)0) + +#define PARSER_THROW(_msg) __EXPR_THROW(Base::ParserError,_msg, (Expression*)0) + +#define PY_THROW(_msg) __EXPR_THROW(Py::RuntimeError,_msg, (Expression*)0) + +//////////////////////////////////////////////////////////////////////////////// + +// WARNING! The following define enables slightly faster any type comparison which +// is not standard conforming, and may break in some rare cases (although not likely) +// +// #define USE_FAST_ANY + +static inline bool is_type(const App::any &value, const std::type_info& t) { +#ifdef USE_FAST_ANY + return &value.type() == &t; +#else + return value.type() == t; +#endif +} + +template +static inline const T &cast(const App::any &value) { +#ifdef USE_FAST_ANY + return *value.cast(); +#else + return App::any_cast(value); +#endif +} + +template +static inline T &cast(App::any &value) { +#ifdef USE_FAST_ANY + return *value.cast(); +#else + return App::any_cast(value); +#endif +} + +template +static inline T &&cast(App::any &&value) { +#ifdef USE_FAST_ANY + return std::move(*value.cast()); +#else + return App::any_cast(std::move(value)); +#endif +} std::string unquote(const std::string & input) { @@ -122,6 +206,454 @@ std::string unquote(const std::string & input) return output; } +//////////////////////////////////////////////////////////////////////////////////// +// +// ExpressionVistor +// +void ExpressionVisitor::getDeps(Expression &e, ExpressionDeps &deps) { + e._getDeps(deps); +} + +void ExpressionVisitor::getDepObjects(Expression &e, + std::set &deps, std::vector *labels) +{ + e._getDepObjects(deps,labels); +} + +void ExpressionVisitor::getIdentifiers(Expression &e, std::set &ids) { + e._getIdentifiers(ids); +} + +bool ExpressionVisitor::adjustLinks(Expression &e, const std::set &inList) { + return e._adjustLinks(inList,*this); +} + +void ExpressionVisitor::importSubNames(Expression &e, const ObjectIdentifier::SubNameMap &subNameMap) { + e._importSubNames(subNameMap); +} + +void ExpressionVisitor::updateLabelReference(Expression &e, + DocumentObject *obj, const std::string &ref, const char *newLabel) +{ + e._updateLabelReference(obj,ref,newLabel); +} + +bool ExpressionVisitor::updateElementReference(Expression &e, App::DocumentObject *feature, bool reverse) { + return e._updateElementReference(feature,reverse,*this); +} + +bool ExpressionVisitor::relabeledDocument( + Expression &e, const std::string &oldName, const std::string &newName) +{ + return e._relabeledDocument(oldName,newName,*this); +} + +bool ExpressionVisitor::renameObjectIdentifier(Expression &e, + const std::map &paths, const ObjectIdentifier &path) +{ + return e._renameObjectIdentifier(paths,path,*this); +} + +void ExpressionVisitor::collectReplacement(Expression &e, + std::map &pathes, + const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const +{ + return e._collectReplacement(pathes,parent,oldObj,newObj); +} + +void ExpressionVisitor::moveCells(Expression &e, const CellAddress &address, int rowCount, int colCount) { + e._moveCells(address,rowCount,colCount,*this); +} + +void ExpressionVisitor::offsetCells(Expression &e, int rowOffset, int colOffset) { + e._offsetCells(rowOffset,colOffset,*this); +} + +///////////////////////////////////////////////////////////////////////////////////// +// Helper functions + +/* The following definitions are from The art of computer programming by Knuth + * (copied from http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison) + */ + +/* +static bool approximatelyEqual(double a, double b, double epsilon) +{ + return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); +} +*/ + +template +static inline bool essentiallyEqual(T a, T b) +{ + static const T _epsilon = std::numeric_limits::epsilon(); + return std::fabs(a - b) <= ( (std::fabs(a) > std::fabs(b) ? std::fabs(b) : std::fabs(a)) * _epsilon); +} + +template +inline bool essentiallyZero(T a) { + return !a; +} + +template<> +inline bool essentiallyZero(double a) { + return essentiallyEqual(a, 0.0); +} + +template<> +inline bool essentiallyZero(float a) { + return essentiallyEqual(a, 0.0f); +} + +template +static inline bool definitelyGreaterThan(T a, T b) +{ + static const T _epsilon = std::numeric_limits::epsilon(); + return (a - b) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * _epsilon); +} + +template +static inline bool definitelyLessThan(T a, T b) +{ + static const T _epsilon = std::numeric_limits::epsilon(); + return (b - a) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * _epsilon); +} + +static inline int essentiallyInteger(double a, long &l, int &i) { + double intpart; + if(std::modf(a,&intpart) == 0.0) { + if(intpart<0.0) { + if(intpart >= INT_MIN) { + i = (int)intpart; + l = i; + return 1; + } + if(intpart >= LONG_MIN) { + l = (long)intpart; + return 2; + } + }else if(intpart <= INT_MAX) { + i = (int)intpart; + l = i; + return 1; + }else if(intpart <= LONG_MAX) { + l = (int)intpart; + return 2; + } + } + return 0; +} + +static inline bool essentiallyInteger(double a, long &l) { + double intpart; + if(std::modf(a,&intpart) == 0.0) { + if(intpart<0.0) { + if(intpart >= LONG_MIN) { + l = (long)intpart; + return true; + } + }else if(intpart <= LONG_MAX) { + l = (long)intpart; + return true; + } + } + return false; +} + +// This class is intended to be contained inside App::any (via a shared_ptr) +// without holding Python global lock +struct PyObjectWrapper { +public: + typedef std::shared_ptr Pointer; + + PyObjectWrapper(PyObject *obj):pyobj(obj) { + Py::_XINCREF(pyobj); + } + ~PyObjectWrapper() { + if(pyobj) { + Base::PyGILStateLocker lock; + Py::_XDECREF(pyobj); + } + } + PyObjectWrapper(const PyObjectWrapper &) = delete; + PyObjectWrapper &operator=(const PyObjectWrapper &) = delete; + + Py::Object get() const { + if(!pyobj) + return Py::Object(); + return Py::Object(const_cast(pyobj)); + } + +private: + PyObject *pyobj; +}; + +static inline PyObjectWrapper::Pointer pyObjectWrap(PyObject *obj) { + return std::make_shared(obj); +} + +static inline bool isAnyPyObject(const App::any &value) { + return is_type(value,typeid(PyObjectWrapper::Pointer)); +} + +static inline Py::Object __pyObjectFromAny(const App::any &value) { + return cast(value)->get(); +} + +static Py::Object _pyObjectFromAny(const App::any &value, const Expression *e) { + if(value.empty()) + return Py::Object(); + else if (isAnyPyObject(value)) + return __pyObjectFromAny(value); + if (is_type(value,typeid(Quantity))) + return Py::Object(new QuantityPy(new Quantity(cast(value)))); + else if (is_type(value,typeid(double))) + return Py::Float(cast(value)); + else if (is_type(value,typeid(float))) + return Py::Float(cast(value)); + else if (is_type(value,typeid(int))) +#if PY_MAJOR_VERSION < 3 + return Py::Int(cast(value)); +#else + return Py::Long(cast(value)); +#endif + else if (is_type(value,typeid(long))) { + long l = cast(value); +#if PY_MAJOR_VERSION < 3 + if(std::abs(l)<=INT_MAX) + return Py::Int(int(l)); +#endif + return Py::Long(cast(value)); + } else if (is_type(value,typeid(bool))) + return Py::Boolean(cast(value)); + else if (is_type(value,typeid(std::string))) + return Py::String(cast(value)); + else if (is_type(value,typeid(const char*))) + return Py::String(cast(value)); + + _EXPR_THROW("Unknown type", e); +} + +namespace App { +Py::Object pyObjectFromAny(const App::any &value) { + return _pyObjectFromAny(value,0); +} + +App::any pyObjectToAny(Py::Object value, bool check) { + + if(value.isNone()) + return App::any(); + + PyObject *pyvalue = value.ptr(); + + if(!check) + return App::any(pyObjectWrap(pyvalue)); + + if (PyObject_TypeCheck(pyvalue, &Base::QuantityPy::Type)) { + Base::QuantityPy * qp = static_cast(pyvalue); + Base::Quantity * q = qp->getQuantityPtr(); + + return App::any(*q); + } + if (PyFloat_Check(pyvalue)) + return App::any(PyFloat_AsDouble(pyvalue)); +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(pyvalue)) + return App::any(PyInt_AsLong(pyvalue)); +#endif + if (PyLong_Check(pyvalue)) + return App::any(PyLong_AsLong(pyvalue)); +#if PY_MAJOR_VERSION < 3 + else if (PyString_Check(pyvalue)) + return App::any(std::string(PyString_AsString(pyvalue))); +#endif + else if (PyUnicode_Check(pyvalue)) { + PyObject * s = PyUnicode_AsUTF8String(pyvalue); + if(!s) + FC_THROWM(Base::ValueError,"Invalid unicode string"); + Py::Object o(s,true); + +#if PY_MAJOR_VERSION >= 3 + return App::any(std::string(PyUnicode_AsUTF8(s))); +#else + return App::any(std::string(PyString_AsString(s))); +#endif + } + else { + return App::any(pyObjectWrap(pyvalue)); + } +} + +bool pyToQuantity(Quantity &q, const Py::Object &pyobj) { + if (PyObject_TypeCheck(*pyobj, &Base::QuantityPy::Type)) + q = *static_cast(*pyobj)->getQuantityPtr(); + else if (PyFloat_Check(*pyobj)) + q = Quantity(PyFloat_AsDouble(*pyobj)); +#if PY_MAJOR_VERSION < 3 + else if (PyInt_Check(*pyobj)) + q = Quantity(PyInt_AsLong(*pyobj)); +#endif + else if (PyLong_Check(*pyobj)) + q = Quantity(PyLong_AsLong(*pyobj)); + else + return false; + return true; +} + +static inline Quantity pyToQuantity(const Py::Object &pyobj, + const Expression *e, const char *msg=0) +{ + Quantity q; + if(!pyToQuantity(q,pyobj)) { + if(!msg) + msg = "Failed to convert to Quantity."; + __EXPR_THROW(TypeError,msg,e); + } + return q; +} + +Py::Object pyFromQuantity(const Quantity &quantity) { + if(!quantity.getUnit().isEmpty()) + return Py::Object(new QuantityPy(new Quantity(quantity))); + double v = quantity.getValue(); + long l; + int i; + switch(essentiallyInteger(v,l,i)) { + case 1: +#if PY_MAJOR_VERSION < 3 + return Py::Int(i); +#endif + case 2: + return Py::Long(l); + default: + return Py::Float(v); + } +} + +Quantity anyToQuantity(const App::any &value, const char *msg) { + if (is_type(value,typeid(Quantity))) { + return cast(value); + } else if (is_type(value,typeid(bool))) { + return Quantity(cast(value)?1.0:0.0); + } else if (is_type(value,typeid(int))) { + return Quantity(cast(value)); + } else if (is_type(value,typeid(long))) { + return Quantity(cast(value)); + } else if (is_type(value,typeid(float))) { + return Quantity(cast(value)); + } else if (is_type(value,typeid(double))) { + return Quantity(cast(value)); + } + if(!msg) + msg = "Failed to convert to Quantity"; + TYPE_THROW(msg); +} + +static inline bool anyToLong(long &res, const App::any &value) { + if (is_type(value,typeid(int))) { + res = cast(value); + } else if (is_type(value,typeid(long))) { + res = cast(value); + } else if (is_type(value,typeid(bool))) + res = cast(value)?1:0; + else + return false; + return true; +} + +static inline bool anyToDouble(double &res, const App::any &value) { + if (is_type(value,typeid(double))) + res = cast(value); + else if (is_type(value,typeid(float))) + res = cast(value); + else if (is_type(value,typeid(long))) + res = cast(value); + else if (is_type(value,typeid(int))) + res = cast(value); + else if (is_type(value,typeid(bool))) + res = cast(value)?1:0; + else + return false; + return true; +} + +bool isAnyEqual(const App::any &v1, const App::any &v2) { + if(v1.empty()) + return v2.empty(); + else if(v2.empty()) + return false; + + if(!is_type(v1,v2.type())) { + if(is_type(v1,typeid(Quantity))) + return cast(v1) == anyToQuantity(v2); + else if(is_type(v2,typeid(Quantity))) + return anyToQuantity(v1) == cast(v2); + + long l1,l2; + double d1,d2; + if(anyToLong(l1,v1)) { + if(anyToLong(l2,v2)) + return l1==l2; + else if(anyToDouble(d2,v2)) + return essentiallyEqual((double)l1,d2); + else + return false; + }else if(anyToDouble(d1,v1)) + return anyToDouble(d2,v2) && essentiallyEqual(d1,d2); + + if(is_type(v1,typeid(std::string))) { + if(is_type(v2,typeid(const char*))) { + auto c = cast(v2); + return c && cast(v1)==c; + } + return false; + }else if(is_type(v1,typeid(const char*))) { + if(is_type(v2,typeid(std::string))) { + auto c = cast(v1); + return c && cast(v2)==c; + } + return false; + } + } + + if (is_type(v1,typeid(int))) + return cast(v1) == cast(v2); + if (is_type(v1,typeid(long))) + return cast(v1) == cast(v2); + if (is_type(v1,typeid(std::string))) + return cast(v1) == cast(v2); + if (is_type(v1,typeid(const char*))) { + auto c1 = cast(v1); + auto c2 = cast(v2); + return c1==c2 || (c1 && c2 && strcmp(c1,c2)==0); + } + if (is_type(v1,typeid(bool))) + return cast(v1) == cast(v2); + if (is_type(v1,typeid(double))) + return essentiallyEqual(cast(v1), cast(v2)); + if (is_type(v1,typeid(float))) + return essentiallyEqual(cast(v1), cast(v2)); + + if (is_type(v1,typeid(Quantity))) + return cast(v1) == cast(v2); + + if (!isAnyPyObject(v1)) + throw Base::TypeError("Unknown type"); + + Base::PyGILStateLocker lock; + Py::Object o1 = __pyObjectFromAny(v1); + Py::Object o2 = __pyObjectFromAny(v2); + if(!o1.isType(o2.type())) + return false; + int res = PyObject_RichCompareBool(o1.ptr(),o2.ptr(),Py_EQ); + if(res<0) + PyException::ThrowException(); + return !!res; +} + +} // namespace App + + // // Expression base-class // @@ -129,7 +661,7 @@ std::string unquote(const std::string & input) TYPESYSTEM_SOURCE_ABSTRACT(App::Expression, Base::BaseClass); Expression::Expression(const DocumentObject *_owner) - : owner(_owner) + : owner(const_cast(_owner)) { } @@ -138,11 +670,244 @@ Expression::~Expression() { } +void Expression::visit(ExpressionVisitor &v) { + _visit(v); + v.visit(*this); +} + Expression * Expression::parse(const DocumentObject *owner, const std::string &buffer) { return ExpressionParser::parse(owner, buffer.c_str()); } +bool Expression::isSame(const Expression &other) const { + if(&other == this) + return true; + if(getTypeId()!=other.getTypeId()) + return false; + return comment==other.comment && toString(true) == other.toString(true); +} + +class GetDepsExpressionVisitor : public ExpressionVisitor { +public: + GetDepsExpressionVisitor(ExpressionDeps &deps) + :deps(deps) + {} + + virtual void visit(Expression &e) { + this->getDeps(e,deps); + } + + ExpressionDeps &deps; +}; + +void Expression::getDeps(ExpressionDeps &deps) const { + GetDepsExpressionVisitor v(deps); + const_cast(this)->visit(v); +} + +ExpressionDeps Expression::getDeps() const { + ExpressionDeps deps; + getDeps(deps); + return deps; +} + +class GetDepObjsExpressionVisitor : public ExpressionVisitor { +public: + GetDepObjsExpressionVisitor(std::set &deps, std::vector *labels) + :deps(deps),labels(labels) + {} + + virtual void visit(Expression &e) { + this->getDepObjects(e,deps,labels); + } + + std::set &deps; + std::vector *labels; +}; + +void Expression::getDepObjects(std::set &deps, std::vector *labels) const { + GetDepObjsExpressionVisitor v(deps,labels); + const_cast(this)->visit(v); +} + +std::set Expression::getDepObjects(std::vector *labels) const { + std::set deps; + getDepObjects(deps,labels); + return deps; +} + +class GetIdentifiersExpressionVisitor : public ExpressionVisitor { +public: + GetIdentifiersExpressionVisitor(std::set &deps) + :deps(deps) + {} + + virtual void visit(Expression &e) { + this->getIdentifiers(e,deps); + } + + std::set &deps; +}; + +void Expression::getIdentifiers(std::set &deps) const { + GetIdentifiersExpressionVisitor v(deps); + const_cast(this)->visit(v); +} + +std::set Expression::getIdentifiers() const { + std::set deps; + getIdentifiers(deps); + return deps; +} + +class AdjustLinksExpressionVisitor : public ExpressionVisitor { +public: + AdjustLinksExpressionVisitor(const std::set &inList) + :inList(inList),res(false) + {} + + virtual void visit(Expression &e) { + if(this->adjustLinks(e,inList)) + res = true; + } + + const std::set &inList; + bool res; +}; + +bool Expression::adjustLinks(const std::set &inList) { + AdjustLinksExpressionVisitor v(inList); + visit(v); + return v.res; +} + +class ImportSubNamesExpressionVisitor : public ExpressionVisitor { +public: + ImportSubNamesExpressionVisitor(const ObjectIdentifier::SubNameMap &subNameMap) + :subNameMap(subNameMap) + {} + + virtual void visit(Expression &e) { + this->importSubNames(e,subNameMap); + } + + const ObjectIdentifier::SubNameMap &subNameMap; +}; + +ExpressionPtr Expression::importSubNames(const std::map &nameMap) const { + if(!owner || !owner->getDocument()) + return 0; + ObjectIdentifier::SubNameMap subNameMap; + for(auto &dep : getDeps()) { + for(auto &info : dep.second) { + for(auto &path : info.second) { + auto obj = path.getDocumentObject(); + if(!obj) + continue; + auto it = nameMap.find(obj->getExportName(true)); + if(it!=nameMap.end()) + subNameMap.emplace(std::make_pair(obj,std::string()),it->second); + auto key = std::make_pair(obj,path.getSubObjectName()); + if(key.second.empty() || subNameMap.count(key)) + continue; + std::string imported = PropertyLinkBase::tryImportSubName( + obj,key.second.c_str(),owner->getDocument(), nameMap); + if(imported.size()) + subNameMap.emplace(std::move(key),std::move(imported)); + } + } + } + if(subNameMap.empty()) + return 0; + ImportSubNamesExpressionVisitor v(subNameMap); + auto res = copy(); + res->visit(v); + return ExpressionPtr(res); +} + +class UpdateLabelExpressionVisitor : public ExpressionVisitor { +public: + UpdateLabelExpressionVisitor(App::DocumentObject *obj, const std::string &ref, const char *newLabel) + :obj(obj),ref(ref),newLabel(newLabel) + {} + + virtual void visit(Expression &e) { + this->updateLabelReference(e,obj,ref,newLabel); + } + + App::DocumentObject *obj; + const std::string &ref; + const char *newLabel; +}; + +ExpressionPtr Expression::updateLabelReference( + App::DocumentObject *obj, const std::string &ref, const char *newLabel) const +{ + if(ref.size()<=2) + return ExpressionPtr(); + std::vector labels; + getDepObjects(&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) { + UpdateLabelExpressionVisitor v(obj,ref,newLabel); + auto expr = copy(); + expr->visit(v); + return ExpressionPtr(expr); + } + } + return ExpressionPtr(); +} + +class ReplaceObjectExpressionVisitor : public ExpressionVisitor { +public: + ReplaceObjectExpressionVisitor(const DocumentObject *parent, + DocumentObject *oldObj, DocumentObject *newObj) + : parent(parent),oldObj(oldObj),newObj(newObj) + { + } + + void visit(Expression &e) { + if(collect) + this->collectReplacement(e,pathes,parent,oldObj,newObj); + else + this->renameObjectIdentifier(e,pathes,dummy); + } + + const DocumentObject *parent; + DocumentObject *oldObj; + DocumentObject *newObj; + ObjectIdentifier dummy; + std::map pathes; + bool collect = true; +}; + +ExpressionPtr Expression::replaceObject(const DocumentObject *parent, + DocumentObject *oldObj, DocumentObject *newObj) const +{ + ReplaceObjectExpressionVisitor v(parent,oldObj,newObj); + + // First pass, collect any changes. We have to const_cast it, as visit() is + // not const. This is ugly... + const_cast(this)->visit(v); + + if(v.pathes.empty()) + return ExpressionPtr(); + + // Now make a copy and do the actual replacement + auto expr = copy(); + v.collect = false; + expr->visit(v); + return ExpressionPtr(expr); +} + +Expression *Expression::copy() const { + auto expr = _copy(); + expr->comment = comment; + return expr; +} + // // UnitExpression class // @@ -198,7 +963,7 @@ Expression *UnitExpression::simplify() const * Return a string representation of the expression. */ -std::string UnitExpression::toString() const +std::string UnitExpression::toString(bool) const { return unitStr; } @@ -207,7 +972,7 @@ std::string UnitExpression::toString() const * Return a copy of the expression. */ -Expression *UnitExpression::copy() const +Expression *UnitExpression::_copy() const { return new UnitExpression(owner, quantity, unitStr); } @@ -251,7 +1016,7 @@ Expression *NumberExpression::simplify() const * Create and return a copy of the expression. */ -Expression *NumberExpression::copy() const +Expression *NumberExpression::_copy() const { return new NumberExpression(owner, quantity); } @@ -270,7 +1035,7 @@ void NumberExpression::negate() quantity.setValue(-quantity.getValue()); } -std::string NumberExpression::toString() const +std::string NumberExpression::toString(bool) const { std::stringstream s; s << std::setprecision(std::numeric_limits::digits10 + 1) << quantity.getValue(); @@ -282,6 +1047,13 @@ std::string NumberExpression::toString() const return s.str(); } +bool NumberExpression::isInteger(long *l) const { + long _l; + if(!l) + l = &_l; + return essentiallyInteger(getValue(),*l); +} + // // OperatorExpression class // @@ -312,31 +1084,6 @@ bool OperatorExpression::isTouched() const return left->isTouched() || right->isTouched(); } -/* The following definitions are from The art of computer programming by Knuth - * (copied from http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison) - */ - -/* -static bool approximatelyEqual(double a, double b, double epsilon) -{ - return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); -} -*/ - -static bool essentiallyEqual(double a, double b, double epsilon) -{ - return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon); -} - -static bool definitelyGreaterThan(double a, double b, double epsilon) -{ - return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); -} - -static bool definitelyLessThan(double a, double b, double epsilon) -{ - return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); -} /** * Evaluate the expression. Returns a new Expression with the result, or throws @@ -350,7 +1097,6 @@ Expression * OperatorExpression::eval() const std::unique_ptr e2(right->eval()); NumberExpression * v2; Expression * output; - const double epsilon = std::numeric_limits::epsilon(); v1 = freecad_dynamic_cast(e1.get()); v2 = freecad_dynamic_cast(e2.get()); @@ -382,34 +1128,34 @@ Expression * OperatorExpression::eval() const case EQ: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the = operator"); - output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue(), epsilon) ); + output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue()) ); break; case NEQ: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the != operator"); - output = new BooleanExpression(owner, !essentiallyEqual(v1->getValue(), v2->getValue(), epsilon) ); + output = new BooleanExpression(owner, !essentiallyEqual(v1->getValue(), v2->getValue()) ); break; case LT: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the < operator"); - output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue(), epsilon) ); + output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue()) ); break; case GT: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the > operator"); - output = new BooleanExpression(owner, definitelyGreaterThan(v1->getValue(), v2->getValue(), epsilon) ); + output = new BooleanExpression(owner, definitelyGreaterThan(v1->getValue(), v2->getValue()) ); break; case LTE: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the <= operator"); - output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue(), epsilon) || - essentiallyEqual(v1->getValue(), v2->getValue(), epsilon)); + output = new BooleanExpression(owner, definitelyLessThan(v1->getValue(), v2->getValue()) || + essentiallyEqual(v1->getValue(), v2->getValue())); break; case GTE: if (v1->getUnit() != v2->getUnit()) throw ExpressionError("Incompatible units for the >= operator"); - output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue(), epsilon) || - definitelyGreaterThan(v1->getValue(), v2->getValue(), epsilon)); + output = new BooleanExpression(owner, essentiallyEqual(v1->getValue(), v2->getValue()) || + definitelyGreaterThan(v1->getValue(), v2->getValue())); break; case NEG: output = new NumberExpression(owner, -v1->getQuantity() ); @@ -454,7 +1200,7 @@ Expression *OperatorExpression::simplify() const * @returns A string representing the expression. */ -std::string OperatorExpression::toString() const +std::string OperatorExpression::toString(bool persistent) const { std::stringstream s; bool needsParens; @@ -474,19 +1220,19 @@ std::string OperatorExpression::toString() const switch (op) { case NEG: - s << "-" << (needsParens ? "(" : "") << left->toString() << (needsParens ? ")" : ""); + s << "-" << (needsParens ? "(" : "") << left->toString(persistent) << (needsParens ? ")" : ""); return s.str(); case POS: - s << "+" << (needsParens ? "(" : "") << left->toString() << (needsParens ? ")" : ""); + s << "+" << (needsParens ? "(" : "") << left->toString(persistent) << (needsParens ? ")" : ""); return s.str(); default: break; } if (needsParens) - s << "(" << left->toString() << ")"; + s << "(" << left->toString(persistent) << ")"; else - s << left->toString(); + s << left->toString(persistent); switch (op) { case ADD: @@ -545,9 +1291,9 @@ std::string OperatorExpression::toString() const } if (needsParens) - s << "(" << right->toString() << ")"; + s << "(" << right->toString(persistent) << ")"; else - s << right->toString(); + s << right->toString(persistent); return s.str(); } @@ -556,7 +1302,7 @@ std::string OperatorExpression::toString() const * A deep copy of the expression. */ -Expression *OperatorExpression::copy() const +Expression *OperatorExpression::_copy() const { return new OperatorExpression(owner, left->copy(), op, right->copy()); } @@ -596,25 +1342,12 @@ int OperatorExpression::priority() const } } -/** - * Compute the expressions dependencies, i.e the properties it relies on. - * - * @param props A set of strings. Each string contains the name of a property that this expression depends on. - */ - -void OperatorExpression::getDeps(std::set &props) const -{ - left->getDeps(props); - right->getDeps(props); -} - -void OperatorExpression::visit(ExpressionVisitor &v) +void OperatorExpression::_visit(ExpressionVisitor &v) { if (left) left->visit(v); if (right) right->visit(v); - v.visit(this); } bool OperatorExpression::isCommutative() const @@ -890,6 +1623,7 @@ Expression * FunctionExpression::evalAggregate() const Property * p = owner->getPropertyByName(range.address().c_str()); PropertyQuantity * qp; PropertyFloat * fp; + PropertyInteger * ip; if (!p) continue; @@ -898,6 +1632,8 @@ Expression * FunctionExpression::evalAggregate() const c->collect(qp->getQuantityValue()); else if ((fp = freecad_dynamic_cast(p)) != 0) c->collect(Quantity(fp->getValue())); + else if ((ip = freecad_dynamic_cast(p)) != 0) + c->collect(Quantity(ip->getValue())); else throw Exception("Invalid property type for aggregate"); } while (range.next()); @@ -1177,12 +1913,12 @@ Expression *FunctionExpression::simplify() const * @returns A string representing the expression. */ -std::string FunctionExpression::toString() const +std::string FunctionExpression::toString(bool persistent) const { std::stringstream ss; for (size_t i = 0; i < args.size(); ++i) { - ss << args[i]->toString(); + ss << args[i]->toString(persistent); if (i != args.size() - 1) ss << "; "; } @@ -1258,7 +1994,7 @@ std::string FunctionExpression::toString() const * @returns A deep copy of the expression. */ -Expression *FunctionExpression::copy() const +Expression *FunctionExpression::_copy() const { std::vector::const_iterator i = args.begin(); std::vector a; @@ -1275,22 +2011,7 @@ int FunctionExpression::priority() const return 20; } -/** - * Compute the dependency set of the expression. The set contains the names - * of all Property objects this expression relies on. - */ - -void FunctionExpression::getDeps(std::set &props) const -{ - std::vector::const_iterator i = args.begin(); - - while (i != args.end()) { - (*i)->getDeps(props); - ++i; - } -} - -void FunctionExpression::visit(ExpressionVisitor &v) +void FunctionExpression::_visit(ExpressionVisitor &v) { std::vector::const_iterator i = args.begin(); @@ -1298,7 +2019,6 @@ void FunctionExpression::visit(ExpressionVisitor &v) (*i)->visit(v); ++i; } - v.visit(this); } // @@ -1326,12 +2046,7 @@ VariableExpression::~VariableExpression() bool VariableExpression::isTouched() const { - try { - return getProperty()->isTouched(); - } - catch (...) { - return false; - } + return var.isTouched(); } /** @@ -1420,10 +2135,21 @@ Expression * VariableExpression::eval() const return new StringExpression(owner, svalue); } + else if (isAnyPyObject(value)) { + Base::PyGILStateLocker lock; + return new PyObjectExpression(owner,__pyObjectFromAny(value).ptr()); + } throw ExpressionError("Property is of invalid type."); } +std::string VariableExpression::toString(bool persistent) const { + if(persistent) + return var.toPersistentString(); + else + return var.toString(); +} + /** * Simplify the expression. Simplification of VariableExpression objects is * not possible (if it is instantiated it would be an evaluation instead). @@ -1440,7 +2166,7 @@ Expression *VariableExpression::simplify() const * Return a copy of the expression. */ -Expression *VariableExpression::copy() const +Expression *VariableExpression::_copy() const { return new VariableExpression(owner, var); } @@ -1450,43 +2176,173 @@ int VariableExpression::priority() const return 20; } -/** - * Compute the dependency of the expression. In this case \a props - * is a set of strings, i.e the names of the Property objects, and - * the variable name this expression relies on is inserted into the set. - * Notice that the variable may be unqualified, i.e without any reference - * to the owning object. This must be taken into consideration when using - * the set. - */ +void VariableExpression::_getDeps(ExpressionDeps &deps) const +{ + auto dep = var.getDep(); + if(dep.first) + deps[dep.first][dep.second].push_back(var); +} -void VariableExpression::getDeps(std::set &props) const +void VariableExpression::_getDepObjects( + std::set &deps, std::vector *labels) const { - props.insert(var); + auto dep = var.getDep(labels); + if(dep.first) + deps.insert(dep.first); } -void VariableExpression::setPath(const ObjectIdentifier &path) +void VariableExpression::_getIdentifiers(std::set &deps) const { - var = path; + deps.insert(var); +} + +bool VariableExpression::_relabeledDocument(const std::string &oldName, + const std::string &newName, ExpressionVisitor &v) +{ + return var.relabeledDocument(v, oldName, newName); } -bool VariableExpression::validDocumentObjectRename(const std::string &oldName, const std::string &newName) +bool VariableExpression::_adjustLinks( + const std::set &inList, ExpressionVisitor &v) { - return var.validDocumentObjectRename(oldName, newName); + return var.adjustLinks(v,inList); } -bool VariableExpression::renameDocumentObject(const std::string &oldName, const std::string &newName) +void VariableExpression::_importSubNames(const ObjectIdentifier::SubNameMap &subNameMap) { - return var.renameDocumentObject(oldName, newName); + var.importSubNames(subNameMap); } -bool VariableExpression::validDocumentRename(const std::string &oldName, const std::string &newName) +void VariableExpression::_updateLabelReference( + App::DocumentObject *obj, const std::string &ref, const char *newLabel) { - return var.validDocumentRename(oldName, newName); + var.updateLabelReference(obj,ref,newLabel); } -bool VariableExpression::renameDocument(const std::string &oldName, const std::string &newName) +bool VariableExpression::_updateElementReference( + App::DocumentObject *feature, bool reverse, ExpressionVisitor &v) { - return var.renameDocument(oldName, newName); + return var.updateElementReference(v,feature,reverse); +} + +bool VariableExpression::_renameObjectIdentifier( + const std::map &paths, + const ObjectIdentifier &path, ExpressionVisitor &v) +{ + const auto &oldPath = var.canonicalPath(); + auto it = paths.find(oldPath); + if (it != paths.end()) { + v.aboutToChange(); + if(path.getOwner()) + var = it->second.relativeTo(path); + else + var = it->second; + return true; + } + return false; +} + +void VariableExpression::_collectReplacement( + std::map &pathes, + const App::DocumentObject *parent, + App::DocumentObject *oldObj, + App::DocumentObject *newObj) const +{ + ObjectIdentifier path; + if(var.replaceObject(path,parent,oldObj,newObj)) + pathes[var.canonicalPath()] = std::move(path); +} + +void VariableExpression::_moveCells(const CellAddress &address, + int rowCount, int colCount, ExpressionVisitor &v) +{ + if(var.hasDocumentObjectName(true)) + return; + + auto &comp = var.getPropertyComponent(0); + CellAddress addr = stringToAddress(comp.getName().c_str(),true); + if(!addr.isValid()) + return; + + int thisRow = addr.row(); + int thisCol = addr.col(); + if (thisRow >= address.row() || thisCol >= address.col()) { + v.aboutToChange(); + addr.setRow(thisRow + rowCount); + addr.setCol(thisCol + colCount); + comp = ObjectIdentifier::SimpleComponent(addr.toString()); + } +} + +void VariableExpression::_offsetCells(int rowOffset, int colOffset, ExpressionVisitor &v) { + if(var.hasDocumentObjectName(true)) + return; + + auto &comp = var.getPropertyComponent(0); + CellAddress addr = stringToAddress(comp.getName().c_str(),true); + if(!addr.isValid() || (addr.isAbsoluteCol() && addr.isAbsoluteRow())) + return; + + v.aboutToChange(); + if(!addr.isAbsoluteCol()) + addr.setCol(addr.col()+colOffset); + if(!addr.isAbsoluteRow()) + addr.setRow(addr.row()+rowOffset); + comp = ObjectIdentifier::SimpleComponent(addr.toString()); +} + +void VariableExpression::setPath(const ObjectIdentifier &path) +{ + var = path; +} + +// +// PyObjectExpression class +// + +TYPESYSTEM_SOURCE(App::PyObjectExpression, App::Expression); + +PyObjectExpression::~PyObjectExpression() { + if(pyObj) { + Base::PyGILStateLocker lock; + Py::_XDECREF(pyObj); + } +} + +Py::Object PyObjectExpression::getPyObject() const { + if(!pyObj) + return Py::Object(); + return Py::Object(pyObj); +} + +void PyObjectExpression::setPyObject(Py::Object obj) { + Py::_XDECREF(pyObj); + pyObj = obj.ptr(); + Py::_XINCREF(pyObj); +} + +void PyObjectExpression::setPyObject(PyObject *obj, bool owned) { + if(pyObj == obj) + return; + Py::_XDECREF(pyObj); + pyObj = obj; + if(!owned) + Py::_XINCREF(pyObj); +} + +std::string PyObjectExpression::toString(bool) const +{ + if(!pyObj) + return "None"; + else { + Base::PyGILStateLocker lock; + return Py::Object(pyObj).as_string(); + } +} + +Expression* PyObjectExpression::_copy() const +{ + return new PyObjectExpression(owner,pyObj,false); } // @@ -1519,7 +2375,7 @@ Expression *StringExpression::simplify() const return copy(); } -std::string StringExpression::toString() const +std::string StringExpression::toString(bool) const { return quote(text); } @@ -1533,7 +2389,7 @@ int StringExpression::priority() const * Return a copy of the expression. */ -Expression *StringExpression::copy() const +Expression *StringExpression::_copy() const { return new StringExpression(owner, text); } @@ -1589,11 +2445,11 @@ Expression *ConditionalExpression::simplify() const } } -std::string ConditionalExpression::toString() const +std::string ConditionalExpression::toString(bool persistent) const { - std::string cstr = condition->toString(); - std::string tstr = trueExpr->toString(); - std::string fstr = falseExpr->toString(); + std::string cstr = condition->toString(persistent); + std::string tstr = trueExpr->toString(persistent); + std::string fstr = falseExpr->toString(persistent); if (trueExpr->priority() <= priority()) tstr = "(" + tstr + ")"; @@ -1604,7 +2460,7 @@ std::string ConditionalExpression::toString() const return cstr + " ? " + tstr + " : " + fstr; } -Expression *ConditionalExpression::copy() const +Expression *ConditionalExpression::_copy() const { return new ConditionalExpression(owner, condition->copy(), trueExpr->copy(), falseExpr->copy()); } @@ -1614,14 +2470,7 @@ int ConditionalExpression::priority() const return 2; } -void ConditionalExpression::getDeps(std::set &props) const -{ - condition->getDeps(props); - trueExpr->getDeps(props); - falseExpr->getDeps(props); -} - -void ConditionalExpression::visit(ExpressionVisitor &v) +void ConditionalExpression::_visit(ExpressionVisitor &v) { condition->visit(v); trueExpr->visit(v); @@ -1636,12 +2485,12 @@ ConstantExpression::ConstantExpression(const DocumentObject *_owner, std::string { } -std::string ConstantExpression::toString() const +std::string ConstantExpression::toString(bool) const { return name; } -Expression *ConstantExpression::copy() const +Expression *ConstantExpression::_copy() const { return new ConstantExpression(owner, name.c_str(), quantity); } @@ -1658,7 +2507,7 @@ BooleanExpression::BooleanExpression(const DocumentObject *_owner, bool _value) { } -Expression *BooleanExpression::copy() const +Expression *BooleanExpression::_copy() const { return new BooleanExpression(owner, getValue() > 0.5 ? true : false); } @@ -1666,14 +2515,13 @@ Expression *BooleanExpression::copy() const TYPESYSTEM_SOURCE(App::RangeExpression, App::Expression); RangeExpression::RangeExpression(const DocumentObject *_owner, const std::string &begin, const std::string &end) - : Expression(_owner) - , range((begin + ":" + end).c_str()) + : Expression(_owner), begin(begin), end(end) { } bool RangeExpression::isTouched() const { - Range i(range); + Range i(getRange()); do { Property * prop = owner->getPropertyByName(i.address().c_str()); @@ -1690,14 +2538,14 @@ Expression *RangeExpression::eval() const throw Exception("Range expression cannot be evaluated"); } -std::string RangeExpression::toString() const +std::string RangeExpression::toString(bool) const { - return range.rangeString(); + return begin + ":" + end; } -Expression *RangeExpression::copy() const +Expression *RangeExpression::_copy() const { - return new RangeExpression(owner, range.fromCellString(), range.toCellString()); + return new RangeExpression(owner, begin, end); } int RangeExpression::priority() const @@ -1705,29 +2553,159 @@ int RangeExpression::priority() const return 20; } -void RangeExpression::getDeps(std::set &props) const +Expression *RangeExpression::simplify() const +{ + return copy(); +} + +void RangeExpression::_getDeps(ExpressionDeps &deps) const { - Range i(range); + assert(owner); + Range i(getRange()); + + auto &dep = deps[owner]; do { - props.insert(ObjectIdentifier(owner, i.address())); + std::string address = i.address(); + dep[address].push_back(ObjectIdentifier(owner,address)); } while (i.next()); } -Expression *RangeExpression::simplify() const +Range RangeExpression::getRange() const +{ + auto c1 = stringToAddress(begin.c_str(),true); + auto c2 = stringToAddress(end.c_str(),true); + if(c1.isValid() && c1.isValid()) + return Range(c1,c2); + + Base::PyGILStateLocker lock; + static const std::string attr("getCellFromAlias"); + Py::Object pyobj(owner->getPyObject(),true); + if(!pyobj.hasAttr(attr)) + EXPR_THROW("Invalid cell range " << begin << ':' << end); + Py::Callable callable(pyobj.getAttr(attr)); + if(!c1.isValid()) { + try { + Py::Tuple arg(1); + arg.setItem(0,Py::String(begin)); + c1 = CellAddress(callable.apply(arg).as_string().c_str()); + } catch(Py::Exception &) { + _EXPR_PY_THROW("Invalid cell address '" << begin << "': ",this); + } catch(Base::Exception &e) { + _EXPR_RETHROW(e,"Invalid cell address '" << begin << "': ",this); + } + } + if(!c2.isValid()) { + try { + Py::Tuple arg(1); + arg.setItem(0,Py::String(end)); + c2 = CellAddress(callable.apply(arg).as_string().c_str()); + } catch(Py::Exception &) { + _EXPR_PY_THROW("Invalid cell address '" << end << "': ", this); + } catch(Base::Exception &e) { + _EXPR_RETHROW(e,"Invalid cell address '" << end << "': ", this); + } + } + return Range(c1,c2); +} + +bool RangeExpression::_renameObjectIdentifier( + const std::map &paths, + const ObjectIdentifier &path, ExpressionVisitor &v) { - return copy(); + (void)path; + bool touched =false; + auto it = paths.find(ObjectIdentifier(owner,begin)); + if (it != paths.end()) { + v.aboutToChange(); + begin = it->second.getPropertyName(); + touched = true; + } + it = paths.find(ObjectIdentifier(owner,end)); + if (it != paths.end()) { + v.aboutToChange(); + end = it->second.getPropertyName(); + touched = true; + } + return touched; } -void RangeExpression::setRange(const Range &r) +void RangeExpression::_moveCells(const CellAddress &address, + int rowCount, int colCount, ExpressionVisitor &v) { - range = r; + CellAddress addr = stringToAddress(begin.c_str(),true); + if(addr.isValid()) { + int thisRow = addr.row(); + int thisCol = addr.col(); + if (thisRow >= address.row() || thisCol >= address.col()) { + v.aboutToChange(); + addr.setRow(thisRow+rowCount); + addr.setCol(thisCol+colCount); + begin = addr.toString(); + } + } + addr = stringToAddress(end.c_str(),true); + if(addr.isValid()) { + int thisRow = addr.row(); + int thisCol = addr.col(); + if (thisRow >= address.row() || thisCol >= address.col()) { + v.aboutToChange(); + addr.setRow(thisRow + rowCount); + addr.setCol(thisCol + colCount); + end = addr.toString(); + } + } +} + +void RangeExpression::_offsetCells(int rowOffset, int colOffset, ExpressionVisitor &v) +{ + CellAddress addr = stringToAddress(begin.c_str(),true); + if(addr.isValid() && (!addr.isAbsoluteRow() || !addr.isAbsoluteCol())) { + v.aboutToChange(); + if(!addr.isAbsoluteRow()) + addr.setRow(addr.row()+rowOffset); + if(!addr.isAbsoluteCol()) + addr.setCol(addr.col()+colOffset); + begin = addr.toString(); + } + addr = stringToAddress(end.c_str(),true); + if(addr.isValid() && (!addr.isAbsoluteRow() || !addr.isAbsoluteCol())) { + v.aboutToChange(); + if(!addr.isAbsoluteRow()) + addr.setRow(addr.row()+rowOffset); + if(!addr.isAbsoluteCol()) + addr.setCol(addr.col()+colOffset); + end = addr.toString(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////// + +static Base::XMLReader *_Reader = 0; +ExpressionParser::ExpressionImporter::ExpressionImporter(Base::XMLReader &reader) { + assert(!_Reader); + _Reader = &reader; +} + +ExpressionParser::ExpressionImporter::~ExpressionImporter() { + assert(_Reader); + _Reader = 0; +} + +Base::XMLReader *ExpressionParser::ExpressionImporter::reader() { + return _Reader; } namespace App { namespace ExpressionParser { +bool isModuleImported(PyObject *module) { + (void)module; + return false; +} + /** * Error function for parser. Throws a generic Base::Exception with the parser error. */ @@ -1943,10 +2921,9 @@ UnitExpression * ExpressionParser::parseUnit(const App::DocumentObject *owner, c if (fraction && fraction->getOperator() == OperatorExpression::DIV) { NumberExpression * nom = freecad_dynamic_cast(fraction->getLeft()); UnitExpression * denom = freecad_dynamic_cast(fraction->getRight()); - const double epsilon = std::numeric_limits::epsilon(); // If not initially a unit expression, but value is equal to 1, it means the expression is something like 1/unit - if (denom && nom && essentiallyEqual(nom->getValue(), 1.0, epsilon)) + if (denom && nom && essentiallyEqual(nom->getValue(), 1.0)) unitExpression = true; } } diff --git a/src/App/Expression.h b/src/App/Expression.h index e21f7415ccb1..f490be5b9995 100644 --- a/src/App/Expression.h +++ b/src/App/Expression.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -41,29 +41,66 @@ class DocumentObject; class Expression; class Document; +typedef std::unique_ptr ExpressionPtr; + +AppExport bool isAnyEqual(const App::any &v1, const App::any &v2); +AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = 0); + +typedef std::map > > ExpressionDeps; class AppExport ExpressionVisitor { public: virtual ~ExpressionVisitor() {} - virtual void visit(Expression * e) = 0; + virtual void visit(Expression &e) = 0; + virtual void aboutToChange() {} + virtual int changed() const { return 0;} + virtual void reset() {} + virtual App::PropertyLinkBase* getPropertyLink() {return 0;} + +protected: + void getIdentifiers(Expression &e, std::set &); + void getDeps(Expression &e, ExpressionDeps &); + void getDepObjects(Expression &e, std::set &, std::vector *); + bool adjustLinks(Expression &e, const std::set &inList); + bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName); + bool renameObjectIdentifier(Expression &e, + const std::map &, const ObjectIdentifier &); + void collectReplacement(Expression &e, std::map &, + const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const; + bool updateElementReference(Expression &e, App::DocumentObject *feature,bool reverse); + void importSubNames(Expression &e, const ObjectIdentifier::SubNameMap &subNameMap); + void updateLabelReference(Expression &e, App::DocumentObject *obj, + const std::string &ref, const char *newLabel); + void moveCells(Expression &e, const CellAddress &address, int rowCount, int colCount); + void offsetCells(Expression &e, int rowOffset, int colOffset); }; template class ExpressionModifier : public ExpressionVisitor { public: ExpressionModifier(P & _prop) - : prop(_prop) { } + : prop(_prop) + , propLink(Base::freecad_dynamic_cast(&prop)) + , signaller(_prop,false) + , _changed(0) + {} virtual ~ExpressionModifier() { } - void setExpressionChanged() { - if (!signaller) - signaller = boost::shared_ptr::AtomicPropertyChange>(AtomicPropertyChangeInterface

::getAtomicPropertyChange(prop)); + virtual void aboutToChange() override{ + ++_changed; + signaller.aboutToChange(); } - bool getChanged() const { return signaller != 0; } + virtual int changed() const override { return _changed; } + + virtual void reset() override {_changed = 0;} + + virtual App::PropertyLinkBase* getPropertyLink() override {return propLink;} protected: P & prop; - boost::shared_ptr::AtomicPropertyChange> signaller; + App::PropertyLinkBase *propLink; + typename AtomicPropertyChangeInterface

::AtomicPropertyChange signaller; + int _changed; }; /** @@ -84,31 +121,78 @@ class AppExport Expression : public Base::BaseClass { virtual Expression * eval() const = 0; - virtual std::string toString() const = 0; + virtual std::string toString(bool persistent=false) const = 0; static Expression * parse(const App::DocumentObject * owner, const std::string& buffer); - virtual Expression * copy() const = 0; + Expression * copy() const; virtual int priority() const { return 0; } - virtual void getDeps(std::set &/*props*/) const { } + void getIdentifiers(std::set &) const; + std::set getIdentifiers() const; + + void getDeps(ExpressionDeps &deps) const; + ExpressionDeps getDeps() const; + + std::set getDepObjects(std::vector *labels=0) const; + void getDepObjects(std::set &, std::vector *labels=0) const; + + ExpressionPtr importSubNames(const std::map &nameMap) const; + + ExpressionPtr updateLabelReference(App::DocumentObject *obj, + const std::string &ref, const char *newLabel) const; + + ExpressionPtr replaceObject(const App::DocumentObject *parent, + App::DocumentObject *oldObj, App::DocumentObject *newObj) const; + + bool adjustLinks(const std::set &inList); virtual Expression * simplify() const = 0; - virtual void visit(ExpressionVisitor & v) { v.visit(this); } + void visit(ExpressionVisitor & v); class Exception : public Base::Exception { public: Exception(const char *sMessage) : Base::Exception(sMessage) { } }; - const App::DocumentObject * getOwner() const { return owner; } + App::DocumentObject * getOwner() const { return owner; } virtual boost::any getValueAsAny() const { static boost::any empty; return empty; } + bool isSame(const Expression &other) const; + + friend ExpressionVisitor; + +protected: + virtual Expression *_copy() const = 0; + virtual void _getDeps(ExpressionDeps &) const {} + virtual void _getDepObjects(std::set &, std::vector *) const {} + virtual void _getIdentifiers(std::set &) const {} + virtual bool _adjustLinks(const std::set &, ExpressionVisitor &) {return false;} + virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &) {return false;} + virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &) {return false;} + virtual void _importSubNames(const ObjectIdentifier::SubNameMap &) {} + virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *) {} + virtual bool _renameObjectIdentifier(const std::map &, + const ObjectIdentifier &, ExpressionVisitor &) {return false;} + virtual void _collectReplacement(std::map &, + const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const + { + (void)parent; + (void)oldObj; + (void)newObj; + } + virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &) {} + virtual void _offsetCells(int, int, ExpressionVisitor &) {} + virtual void _visit(ExpressionVisitor &) {} + protected: - const App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */ + App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */ + +public: + std::string comment; }; /** @@ -125,9 +209,9 @@ class AppExport UnitExpression : public Expression { virtual Expression * simplify() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; @@ -163,13 +247,15 @@ class AppExport NumberExpression : public UnitExpression { virtual Expression * simplify() const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; void negate(); - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; + + bool isInteger(long *v=0) const; protected: }; @@ -179,9 +265,9 @@ class AppExport ConstantExpression : public NumberExpression { public: ConstantExpression(const App::DocumentObject *_owner = 0, std::string _name = "", const Base::Quantity &_quantity = Base::Quantity()); - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; @@ -196,7 +282,7 @@ class AppExport BooleanExpression : public NumberExpression { public: BooleanExpression(const App::DocumentObject *_owner = 0, bool _value = false); - virtual Expression * copy() const; + virtual Expression * _copy() const; }; @@ -236,15 +322,13 @@ class AppExport OperatorExpression : public UnitExpression { virtual Expression * simplify() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; - virtual void getDeps(std::set &props) const; - - virtual void visit(ExpressionVisitor & v); + virtual void _visit(ExpressionVisitor & v); Operator getOperator() const { return op; } @@ -278,15 +362,13 @@ class AppExport ConditionalExpression : public Expression { virtual Expression * simplify() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; - virtual void getDeps(std::set &props) const; - - virtual void visit(ExpressionVisitor & v); + virtual void _visit(ExpressionVisitor & v); protected: @@ -355,15 +437,13 @@ class AppExport FunctionExpression : public UnitExpression { virtual Expression * simplify() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; - virtual void getDeps(std::set &props) const; - - virtual void visit(ExpressionVisitor & v); + virtual void _visit(ExpressionVisitor & v); protected: Expression *evalAggregate() const; @@ -393,33 +473,71 @@ class AppExport VariableExpression : public UnitExpression { virtual Expression * simplify() const; - virtual std::string toString() const { return var.toString(); } + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; - virtual void getDeps(std::set &props) const; - std::string name() const { return var.getPropertyName(); } ObjectIdentifier getPath() const { return var; } void setPath(const ObjectIdentifier & path); - bool validDocumentObjectRename(const std::string & oldName, const std::string & newName); + const App::Property *getProperty() const; - bool renameDocumentObject(const std::string & oldName, const std::string & newName); +protected: + virtual void _getDeps(ExpressionDeps &) const; + virtual void _getDepObjects(std::set &, std::vector *) const; + virtual void _getIdentifiers(std::set &) const; + virtual bool _adjustLinks(const std::set &, ExpressionVisitor &); + virtual void _importSubNames(const ObjectIdentifier::SubNameMap &); + virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *); + virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &); + virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &); + virtual bool _renameObjectIdentifier(const std::map &, + const ObjectIdentifier &, ExpressionVisitor &); + virtual void _collectReplacement(std::map &, + const App::DocumentObject *parent, App::DocumentObject *oldObj, + App::DocumentObject *newObj) const; + virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &); + virtual void _offsetCells(int, int, ExpressionVisitor &); - bool validDocumentRename(const std::string &oldName, const std::string &newName); +protected: - bool renameDocument(const std::string &oldName, const std::string &newName); + ObjectIdentifier var; /**< Variable name */ +}; - const App::Property *getProperty() const; +////////////////////////////////////////////////////////////////////// + +class AppExport PyObjectExpression : public Expression { + TYPESYSTEM_HEADER(); + +public: + PyObjectExpression(const App::DocumentObject *_owner=0, PyObject *pyobj=0, bool owned=false) + :Expression(_owner) + { + setPyObject(pyobj,owned); + } + + virtual ~PyObjectExpression(); + + Py::Object getPyObject() const; + + void setPyObject(Py::Object pyobj); + void setPyObject(PyObject *pyobj, bool owned=false); + + virtual std::string toString(bool) const; + + virtual Expression * eval() const { return copy(); } + virtual Expression * simplify() const { return copy(); } protected: + virtual Expression* _copy() const; - ObjectIdentifier var; /**< Variable name */ +protected: + PyObject *pyObj = 0; }; /** @@ -436,13 +554,13 @@ class AppExport StringExpression : public Expression { virtual Expression * simplify() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; virtual std::string getText() const { return text; } virtual int priority() const; - virtual Expression * copy() const; + virtual Expression * _copy() const; protected: @@ -460,22 +578,26 @@ class AppExport RangeExpression : public App::Expression { virtual Expression * eval() const; - virtual std::string toString() const; + virtual std::string toString(bool persistent=false) const; - virtual Expression * copy() const; + virtual Expression * _copy() const; virtual int priority() const; - virtual void getDeps(std::set &props) const; - virtual App::Expression * simplify() const; - Range getRange() const { return range; } + Range getRange() const; - void setRange(const Range & r); +protected: + virtual void _getDeps(ExpressionDeps &) const; + virtual bool _renameObjectIdentifier(const std::map &, + const ObjectIdentifier &, ExpressionVisitor &); + virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &); + virtual void _offsetCells(int, int, ExpressionVisitor &); protected: - Range range; + std::string begin; + std::string end; }; namespace ExpressionParser { @@ -486,6 +608,16 @@ AppExport bool isTokenAnIndentifier(const std::string & str); AppExport bool isTokenAUnit(const std::string & str); AppExport std::vector > tokenize(const std::string & str); +/// Convenient class to mark begin of importing +class AppExport ExpressionImporter { +public: + ExpressionImporter(Base::XMLReader &reader); + ~ExpressionImporter(); + static Base::XMLReader *reader(); +}; + +AppExport bool isModuleImported(PyObject *); + /** * @brief The semantic_type class encapsulates the value in the parse tree during parsing. */ diff --git a/src/App/ExpressionParser.tab.c b/src/App/ExpressionParser.tab.c index 4d5d9d1868d8..a1ae17ad86a5 100644 --- a/src/App/ExpressionParser.tab.c +++ b/src/App/ExpressionParser.tab.c @@ -382,18 +382,18 @@ union yyalloc #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ -#define YYFINAL 34 +#define YYFINAL 38 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 188 +#define YYLAST 196 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 40 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 14 /* YYNRULES -- Number of rules. */ -#define YYNRULES 73 +#define YYNRULES 74 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 130 +#define YYNSTATES 132 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ @@ -446,10 +446,10 @@ static const yytype_uint8 yyrline[] = 76, 77, 78, 79, 80, 81, 82, 83, 84, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 100, 101, 102, 103, 106, 107, 108, 109, 110, 111, 114, - 115, 116, 117, 118, 119, 122, 126, 131, 136, 144, - 145, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, - 172, 173, 176, 177 + 115, 116, 117, 118, 119, 122, 126, 131, 136, 141, + 149, 150, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 177, 178, 181, 182 }; #endif @@ -481,12 +481,12 @@ static const yytype_uint16 yytoknum[] = }; # endif -#define YYPACT_NINF -100 +#define YYPACT_NINF -102 #define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-100))) + (!!((Yystate) == (-102))) -#define YYTABLE_NINF -74 +#define YYTABLE_NINF -75 #define yytable_value_is_error(Yytable_value) \ 0 @@ -495,19 +495,20 @@ static const yytype_uint16 yytoknum[] = STATE-NUM. */ static const yytype_int16 yypact[] = { - 61, 79, -100, -100, 137, -100, -100, -100, -28, 106, - 104, 104, 61, 18, 154, -1, 27, 89, -100, -100, - 16, 26, -15, -17, 104, 154, 143, -100, 7, 86, - -100, -100, 112, 47, -100, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 61, 104, -1, 31, 104, -1, - -1, 43, 146, 22, 23, 24, -100, 79, 79, 39, - -100, -100, -100, -100, 33, -100, 42, 56, -100, -100, - 154, 154, 154, 154, 154, 154, 128, 128, 72, 72, - 31, -100, 135, 31, 31, 48, -100, 81, -100, -100, - 69, -100, -100, -100, -100, -100, -100, 154, -100, 154, - -100, 7, 127, 84, 96, 98, 104, -100, 22, -100, - 101, 119, 132, 7, 7, 7, 73, -100, 148, 149, - 152, -100, -100, -100, 7, 7, 7, -100, -100, -100 + 67, 92, -102, -102, 131, -102, -102, -102, -32, -2, + 117, 117, 67, 3, 24, 167, -4, 15, 86, -102, + -102, 23, 45, -7, -18, 117, 167, 153, -102, 73, + 74, -102, -102, 125, 60, 19, -102, -102, -102, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 67, 117, + -4, 59, 117, -4, -4, 4, 101, 3, 13, 26, + -102, 92, 92, 30, -102, -102, -102, -102, 55, -102, + 75, 77, -102, -102, 167, 167, 167, 167, 167, 167, + 103, 103, 90, 90, 59, -102, 148, 59, 59, 29, + -102, -102, -102, 95, -102, -102, -102, -102, -102, 167, + -102, 167, -102, 73, 140, 97, 109, 111, 117, -102, + 3, -102, 116, 126, 132, 73, 73, 73, 38, -102, + 120, 134, 154, -102, -102, -102, 73, 73, 73, -102, + -102, -102 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. @@ -515,33 +516,34 @@ static const yytype_int16 yypact[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 0, 0, 19, 20, 51, 39, 21, 22, 52, 6, - 0, 0, 0, 0, 2, 4, 0, 3, 7, 45, - 0, 0, 51, 52, 0, 23, 0, 24, 0, 0, - 8, 9, 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 17, 0, 0, 60, - 62, 61, 59, 50, 0, 49, 0, 0, 16, 44, - 33, 34, 35, 36, 37, 38, 11, 10, 12, 13, - 14, 15, 0, 41, 40, 0, 42, 51, 72, 47, - 0, 52, 46, 32, 31, 30, 29, 25, 27, 26, - 28, 0, 0, 56, 55, 53, 0, 43, 0, 69, - 0, 0, 0, 0, 0, 0, 18, 48, 66, 65, - 63, 58, 57, 54, 0, 0, 0, 68, 67, 64 + 0, 0, 19, 20, 52, 39, 21, 22, 53, 6, + 0, 0, 0, 0, 0, 2, 4, 0, 3, 7, + 45, 0, 0, 52, 53, 0, 23, 0, 24, 0, + 0, 8, 9, 0, 0, 52, 53, 46, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 17, 0, 0, 61, 63, 62, 60, 51, 0, 50, + 0, 0, 16, 44, 33, 34, 35, 36, 37, 38, + 11, 10, 12, 13, 14, 15, 0, 41, 40, 0, + 42, 73, 48, 0, 47, 32, 31, 30, 29, 25, + 27, 26, 28, 0, 0, 57, 56, 54, 0, 43, + 0, 70, 0, 0, 0, 0, 0, 0, 18, 49, + 67, 66, 64, 59, 58, 55, 0, 0, 0, 69, + 68, 65 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -100, -100, 0, -100, -100, 129, -100, 5, -100, -39, - -49, -99, -100, 130 + -102, -102, 0, -102, -102, 44, -102, 5, -102, -35, + -6, -101, -102, 128 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { - -1, 13, 32, 15, 26, 27, 16, 33, 18, 67, - 19, 62, 20, 21 + -1, 14, 33, 16, 27, 28, 17, 34, 19, 71, + 20, 66, 21, 22 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If @@ -549,48 +551,50 @@ static const yytype_int8 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int16 yytable[] = { - 14, 25, 109, 89, 92, 17, 5, 55, -73, 54, - 30, 31, 86, 59, 121, 122, 123, 60, 34, -73, - 47, 28, -71, 29, 61, 127, 128, 129, 87, 93, - 95, 46, 91, 94, 96, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 81, 107, 63, 82, 80, - 48, 65, 63, 52, 83, 84, 65, 97, 99, 117, - 51, 85, 53, 112, 1, 2, 3, 4, 5, 6, - 7, 8, 103, 49, 50, 101, 51, 102, 9, 10, - 69, 104, 1, 2, 3, 22, 11, 6, 7, 23, - 63, 41, 64, 12, 65, 105, 9, 10, 42, 43, - 44, 45, 45, 66, 11, 108, 116, 1, 2, 3, - 4, 24, 6, 7, 8, 49, 50, 28, 51, 29, - 113, 9, 10, 35, 36, 37, 38, 39, 40, 11, - 41, 63, 114, 110, 115, 65, 24, 42, 43, 44, - 118, 45, -72, -70, 111, 68, 35, 36, 37, 38, - 39, 40, 87, 41, 43, 44, 8, 45, 119, 106, - 42, 43, 44, 88, 45, 35, 36, 37, 38, 39, - 40, 120, 41, 28, -71, 29, 56, 57, 58, 42, - 43, 44, 90, 45, 124, 125, 98, 100, 126 + 15, 26, 111, 5, -74, 18, 59, 37, 67, 35, + 31, 32, 69, 36, 123, 124, 125, 58, -74, 95, + 90, 51, 89, 96, 38, 129, 130, 131, 50, 29, + -72, 30, 97, 67, -73, -71, 98, 69, 52, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 85, + 92, 94, 86, 84, 109, 29, 45, 30, 87, 88, + 56, 99, 101, 46, 47, 48, 103, 49, 104, 114, + 1, 2, 3, 4, 5, 6, 7, 8, 67, 63, + 68, 57, 69, 64, 9, 10, 53, 54, 55, 55, + 65, 70, 11, 73, 105, 1, 2, 3, 23, 12, + 6, 7, 24, 13, 119, 100, 102, 35, 118, 9, + 10, 8, 53, 54, 106, 55, 107, 11, 91, 49, + 1, 2, 3, 4, 25, 6, 7, 8, 13, 47, + 48, 110, 49, 115, 9, 10, 39, 40, 41, 42, + 43, 44, 11, 45, 67, 116, 112, 117, 69, 25, + 46, 47, 48, 13, 49, 120, 126, 113, 72, 39, + 40, 41, 42, 43, 44, 121, 45, 29, -72, 30, + 127, 122, 108, 46, 47, 48, 0, 49, 39, 40, + 41, 42, 43, 44, 93, 45, 60, 61, 62, 0, + 128, 0, 46, 47, 48, 0, 49 }; -static const yytype_uint8 yycheck[] = +static const yytype_int16 yycheck[] = { - 0, 1, 101, 52, 53, 0, 7, 24, 36, 24, - 10, 11, 51, 6, 113, 114, 115, 10, 0, 36, - 15, 36, 37, 38, 17, 124, 125, 126, 6, 6, - 6, 32, 10, 10, 10, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 85, 4, 48, 44, - 23, 8, 4, 37, 49, 50, 8, 57, 58, 108, - 29, 18, 36, 102, 3, 4, 5, 6, 7, 8, - 9, 10, 39, 26, 27, 36, 29, 38, 17, 18, - 33, 39, 3, 4, 5, 6, 25, 8, 9, 10, - 4, 18, 6, 32, 8, 39, 17, 18, 25, 26, - 27, 29, 29, 17, 25, 36, 106, 3, 4, 5, - 6, 32, 8, 9, 10, 26, 27, 36, 29, 38, - 36, 17, 18, 11, 12, 13, 14, 15, 16, 25, - 18, 4, 36, 6, 36, 8, 32, 25, 26, 27, - 39, 29, 36, 37, 17, 33, 11, 12, 13, 14, - 15, 16, 6, 18, 26, 27, 10, 29, 39, 24, - 25, 26, 27, 17, 29, 11, 12, 13, 14, 15, - 16, 39, 18, 36, 37, 38, 33, 34, 35, 25, - 26, 27, 52, 29, 36, 36, 57, 58, 36 + 0, 1, 103, 7, 36, 0, 24, 13, 4, 6, + 10, 11, 8, 10, 115, 116, 117, 24, 36, 6, + 55, 16, 18, 10, 0, 126, 127, 128, 32, 36, + 37, 38, 6, 4, 36, 37, 10, 8, 23, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 56, 57, 52, 48, 89, 36, 18, 38, 53, 54, + 37, 61, 62, 25, 26, 27, 36, 29, 38, 104, + 3, 4, 5, 6, 7, 8, 9, 10, 4, 6, + 6, 36, 8, 10, 17, 18, 26, 27, 29, 29, + 17, 17, 25, 33, 39, 3, 4, 5, 6, 32, + 8, 9, 10, 36, 110, 61, 62, 6, 108, 17, + 18, 10, 26, 27, 39, 29, 39, 25, 17, 29, + 3, 4, 5, 6, 32, 8, 9, 10, 36, 26, + 27, 36, 29, 36, 17, 18, 11, 12, 13, 14, + 15, 16, 25, 18, 4, 36, 6, 36, 8, 32, + 25, 26, 27, 36, 29, 39, 36, 17, 33, 11, + 12, 13, 14, 15, 16, 39, 18, 36, 37, 38, + 36, 39, 24, 25, 26, 27, -1, 29, 11, 12, + 13, 14, 15, 16, 56, 18, 33, 34, 35, -1, + 36, -1, 25, 26, 27, -1, 29 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -598,18 +602,19 @@ static const yytype_uint8 yycheck[] = static const yytype_uint8 yystos[] = { 0, 3, 4, 5, 6, 7, 8, 9, 10, 17, - 18, 25, 32, 41, 42, 43, 46, 47, 48, 50, - 52, 53, 6, 10, 32, 42, 44, 45, 36, 38, - 42, 42, 42, 47, 0, 11, 12, 13, 14, 15, - 16, 18, 25, 26, 27, 29, 32, 47, 23, 26, - 27, 29, 37, 36, 24, 24, 33, 34, 35, 6, - 10, 17, 51, 4, 6, 8, 17, 49, 33, 33, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 47, 42, 42, 47, 47, 18, 49, 6, 17, 50, - 53, 10, 50, 6, 10, 6, 10, 42, 45, 42, - 45, 36, 38, 39, 39, 39, 24, 49, 36, 51, - 6, 17, 49, 36, 36, 36, 42, 50, 39, 39, - 39, 51, 51, 51, 36, 36, 36, 51, 51, 51 + 18, 25, 32, 36, 41, 42, 43, 46, 47, 48, + 50, 52, 53, 6, 10, 32, 42, 44, 45, 36, + 38, 42, 42, 42, 47, 6, 10, 50, 0, 11, + 12, 13, 14, 15, 16, 18, 25, 26, 27, 29, + 32, 47, 23, 26, 27, 29, 37, 36, 24, 24, + 33, 34, 35, 6, 10, 17, 51, 4, 6, 8, + 17, 49, 33, 33, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 47, 42, 42, 47, 47, 18, + 49, 17, 50, 53, 50, 6, 10, 6, 10, 42, + 45, 42, 45, 36, 38, 39, 39, 39, 24, 49, + 36, 51, 6, 17, 49, 36, 36, 36, 42, 50, + 39, 39, 39, 51, 51, 51, 36, 36, 36, 51, + 51, 51 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ @@ -619,10 +624,10 @@ static const yytype_uint8 yyr1[] = 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, - 47, 47, 47, 47, 47, 48, 48, 48, 48, 49, - 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 52, 52, 53, 53 + 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, + 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 52, 52, 53, 53 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ @@ -632,10 +637,10 @@ static const yytype_uint8 yyr2[] = 3, 3, 3, 3, 3, 3, 3, 3, 5, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, - 3, 3, 3, 4, 3, 1, 3, 3, 5, 1, - 1, 1, 1, 4, 6, 4, 4, 6, 6, 3, - 1, 1, 1, 4, 6, 4, 4, 6, 6, 3, - 1, 1, 1, 1 + 3, 3, 3, 4, 3, 1, 2, 3, 3, 5, + 1, 1, 1, 1, 4, 6, 4, 4, 6, 6, + 3, 1, 1, 1, 4, 6, 4, 4, 6, 6, + 3, 1, 1, 1, 1 }; @@ -1061,25 +1066,25 @@ yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) case 42: /* exp */ #line 59 "ExpressionParser.y" /* yacc.c:1257 */ { delete ((*yyvaluep).expr); } -#line 1065 "ExpressionParser.tab.c" /* yacc.c:1257 */ +#line 1070 "ExpressionParser.tab.c" /* yacc.c:1257 */ break; case 44: /* args */ #line 60 "ExpressionParser.y" /* yacc.c:1257 */ { std::vector::const_iterator i = ((*yyvaluep).arguments).begin(); while (i != ((*yyvaluep).arguments).end()) { delete *i; ++i; } } -#line 1071 "ExpressionParser.tab.c" /* yacc.c:1257 */ +#line 1076 "ExpressionParser.tab.c" /* yacc.c:1257 */ break; case 46: /* cond */ #line 59 "ExpressionParser.y" /* yacc.c:1257 */ { delete ((*yyvaluep).expr); } -#line 1077 "ExpressionParser.tab.c" /* yacc.c:1257 */ +#line 1082 "ExpressionParser.tab.c" /* yacc.c:1257 */ break; case 47: /* unit_exp */ #line 59 "ExpressionParser.y" /* yacc.c:1257 */ { delete ((*yyvaluep).expr); } -#line 1083 "ExpressionParser.tab.c" /* yacc.c:1257 */ +#line 1088 "ExpressionParser.tab.c" /* yacc.c:1257 */ break; @@ -1343,259 +1348,259 @@ yyparse (void) case 2: #line 66 "ExpressionParser.y" /* yacc.c:1646 */ { ScanResult = (yyvsp[0].expr); valueExpression = true; } -#line 1347 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1352 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 3: #line 67 "ExpressionParser.y" /* yacc.c:1646 */ { ScanResult = (yyvsp[0].expr); unitExpression = true; } -#line 1353 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1358 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 4: #line 70 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); } -#line 1359 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1364 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 5: #line 71 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-1].expr), OperatorExpression::UNIT, (yyvsp[0].expr)); } -#line 1365 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1370 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 6: #line 72 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new StringExpression(DocumentObject, (yyvsp[0].string)); } -#line 1371 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1376 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 7: #line 73 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new VariableExpression(DocumentObject, (yyvsp[0].path)); } -#line 1377 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1382 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 8: #line 74 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1))); } -#line 1383 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1388 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 9: #line 75 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[0].expr), OperatorExpression::POS, new NumberExpression(DocumentObject, Quantity(1))); } -#line 1389 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1394 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 10: #line 76 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::ADD, (yyvsp[0].expr)); } -#line 1395 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1400 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 11: #line 77 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::SUB, (yyvsp[0].expr)); } -#line 1401 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1406 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 12: #line 78 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); } -#line 1407 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1412 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 13: #line 79 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); } -#line 1413 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1418 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 14: #line 80 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); } -#line 1419 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1424 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 15: #line 81 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, (yyvsp[0].expr)); } -#line 1425 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1430 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 16: #line 82 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[-1].expr); } -#line 1431 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1436 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 17: #line 83 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new FunctionExpression(DocumentObject, (yyvsp[-2].func), (yyvsp[-1].arguments)); } -#line 1437 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1442 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 18: #line 84 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new ConditionalExpression(DocumentObject, (yyvsp[-4].expr), (yyvsp[-2].expr), (yyvsp[0].expr)); } -#line 1443 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1448 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 19: #line 87 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); } -#line 1449 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1454 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 20: #line 88 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new NumberExpression(DocumentObject, Quantity((yyvsp[0].fvalue))); } -#line 1455 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1460 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 21: #line 89 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))); } -#line 1461 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1466 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 22: #line 90 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new ConstantExpression(DocumentObject, (yyvsp[0].constant).name, Quantity((yyvsp[0].constant).fvalue)); } -#line 1467 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1472 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 23: #line 92 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.arguments).push_back((yyvsp[0].expr)); } -#line 1473 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1478 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 24: #line 93 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.arguments).push_back((yyvsp[0].expr)); } -#line 1479 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1484 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 25: #line 94 "ExpressionParser.y" /* yacc.c:1646 */ { (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); } -#line 1485 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1490 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 26: #line 95 "ExpressionParser.y" /* yacc.c:1646 */ { (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); } -#line 1491 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1496 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 27: #line 96 "ExpressionParser.y" /* yacc.c:1646 */ { (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); } -#line 1497 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1502 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 28: #line 97 "ExpressionParser.y" /* yacc.c:1646 */ { (yyvsp[-2].arguments).push_back((yyvsp[0].expr)); (yyval.arguments) = (yyvsp[-2].arguments); } -#line 1503 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1508 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 29: #line 100 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); } -#line 1509 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1514 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 30: #line 101 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); } -#line 1515 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1520 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 31: #line 102 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); } -#line 1521 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1526 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 32: #line 103 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new RangeExpression(DocumentObject, (yyvsp[-2].string), (yyvsp[0].string)); } -#line 1527 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1532 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 33: #line 106 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::EQ, (yyvsp[0].expr)); } -#line 1533 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1538 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 34: #line 107 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::NEQ, (yyvsp[0].expr)); } -#line 1539 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1544 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 35: #line 108 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LT, (yyvsp[0].expr)); } -#line 1545 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1550 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 36: #line 109 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GT, (yyvsp[0].expr)); } -#line 1551 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1556 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 37: #line 110 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::GTE, (yyvsp[0].expr)); } -#line 1557 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1562 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 38: #line 111 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::LTE, (yyvsp[0].expr)); } -#line 1563 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1568 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 39: #line 114 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new UnitExpression(DocumentObject, (yyvsp[0].quantity).scaler, (yyvsp[0].quantity).unitStr ); } -#line 1569 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1574 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 40: #line 115 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::DIV, (yyvsp[0].expr)); } -#line 1575 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1580 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 41: #line 116 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::MUL, (yyvsp[0].expr)); } -#line 1581 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1586 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 42: #line 117 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-2].expr), OperatorExpression::POW, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue)))); } -#line 1587 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1592 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 43: #line 118 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = new OperatorExpression(DocumentObject, (yyvsp[-3].expr), OperatorExpression::POW, new OperatorExpression(DocumentObject, new NumberExpression(DocumentObject, Quantity((double)(yyvsp[0].ivalue))), OperatorExpression::NEG, new NumberExpression(DocumentObject, Quantity(-1)))); } -#line 1593 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1598 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 44: #line 119 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[-1].expr); } -#line 1599 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1604 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 45: @@ -1603,193 +1608,212 @@ yyparse (void) { /* Path to property within document object */ (yyval.path) = ObjectIdentifier(DocumentObject); (yyval.path).addComponents((yyvsp[0].components)); + (yyval.path).resolveAmbiguity(); } -#line 1608 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1613 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 46: #line 126 "ExpressionParser.y" /* yacc.c:1646 */ - { /* Path to property within document object */ - (yyval.path) = ObjectIdentifier(DocumentObject); - (yyval.path).setDocumentObjectName((yyvsp[-2].string_or_identifier), true); + { /* Path to property of the current document object */ + (yyval.path) = ObjectIdentifier(DocumentObject,true); + (yyval.path).setDocumentObjectName(DocumentObject); (yyval.path).addComponents((yyvsp[0].components)); } -#line 1618 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1623 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 47: #line 131 "ExpressionParser.y" /* yacc.c:1646 */ - { /* Path to property from an external document, within a named document object */ + { /* Path to property within document object */ (yyval.path) = ObjectIdentifier(DocumentObject); - (yyval.path).setDocumentName((yyvsp[-2].string_or_identifier), true); + (yyvsp[-2].string_or_identifier).checkImport(DocumentObject); + (yyval.path).addComponent(ObjectIdentifier::SimpleComponent((yyvsp[-2].string_or_identifier))); (yyval.path).addComponents((yyvsp[0].components)); + (yyval.path).resolveAmbiguity(); } -#line 1628 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1633 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 48: #line 136 "ExpressionParser.y" /* yacc.c:1646 */ { /* Path to property from an external document, within a named document object */ (yyval.path) = ObjectIdentifier(DocumentObject); - (yyval.path).setDocumentName((yyvsp[-4].string_or_identifier), true); - (yyval.path).setDocumentObjectName((yyvsp[-2].string_or_identifier), true); + (yyval.path).setDocumentName(std::move(yyvsp[-2].string_or_identifier), true); + if((yyvsp[0].components).size()) { + (yyval.path).setDocumentObjectName(ObjectIdentifier::String((yyvsp[0].components).front().getName(),false,true), true); + (yyvsp[0].components).pop_front(); + } (yyval.path).addComponents((yyvsp[0].components)); + (yyval.path).resolveAmbiguity(); } -#line 1639 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1643 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 49: -#line 144 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.ivalue) = (yyvsp[0].ivalue); } -#line 1645 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 141 "ExpressionParser.y" /* yacc.c:1646 */ + { /* Path to property from an external document, within a named document object */ + (yyval.path) = ObjectIdentifier(DocumentObject); + (yyval.path).setDocumentName(std::move(yyvsp[-4].string_or_identifier), true); + (yyval.path).setDocumentObjectName(std::move(yyvsp[-2].string_or_identifier), true); + (yyval.path).addComponents((yyvsp[0].components)); + (yyval.path).resolveAmbiguity(); + } +#line 1654 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 50: -#line 145 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.ivalue) = (yyvsp[0].fvalue); } -#line 1651 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 149 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.ivalue) = (yyvsp[0].ivalue); } +#line 1660 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 51: -#line 149 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); } -#line 1657 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 150 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.ivalue) = (yyvsp[0].fvalue); } +#line 1666 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 52: -#line 150 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); } -#line 1663 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 154 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); } +#line 1672 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 53: -#line 151 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-3].string), (yyvsp[-1].ivalue))); } -#line 1669 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 155 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); } +#line 1678 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 54: -#line 152 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-5].string), (yyvsp[-3].ivalue))); (yyval.components) = (yyvsp[0].components); } -#line 1675 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 156 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-1].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1684 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 55: -#line 153 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), ObjectIdentifier::String((yyvsp[-1].string), true))); } -#line 1681 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 157 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-3].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1690 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 56: -#line 154 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), (yyvsp[-1].string))); } -#line 1687 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 158 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-1].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1696 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 57: -#line 155 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components) = (yyvsp[0].components); } -#line 1693 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 159 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::MapComponent((yyvsp[-1].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1702 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 58: -#line 156 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), (yyvsp[-3].string))); (yyval.components) = (yyvsp[0].components); } -#line 1699 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 160 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1708 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 59: -#line 157 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); } -#line 1705 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 161 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent((yyvsp[-3].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1714 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 60: -#line 160 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); } -#line 1711 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 162 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); } +#line 1720 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 61: -#line 161 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); } -#line 1717 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 165 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); } +#line 1726 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 62: -#line 162 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[0].string))); } -#line 1723 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 166 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); } +#line 1732 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 63: -#line 163 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-3].string), (yyvsp[-1].ivalue))); } -#line 1729 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 167 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[0].string))); } +#line 1738 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 64: -#line 164 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::ArrayComponent((yyvsp[-5].string), (yyvsp[-3].ivalue))); (yyval.components) = (yyvsp[0].components); } -#line 1735 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 168 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-1].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1744 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 65: -#line 165 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), ObjectIdentifier::String((yyvsp[-1].string), true))); } -#line 1741 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 169 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::ArrayComponent((yyvsp[-3].ivalue))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1750 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 66: -#line 166 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-3].string), (yyvsp[-1].string))); } -#line 1747 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 170 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-1].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1756 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 67: -#line 167 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components) = (yyvsp[0].components); } -#line 1753 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 171 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.components).push_front(ObjectIdentifier::MapComponent((yyvsp[-1].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-3].string))); } +#line 1762 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 68: -#line 168 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::MapComponent((yyvsp[-5].string), (yyvsp[-3].string))); (yyval.components) = (yyvsp[0].components); } -#line 1759 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 172 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String((yyvsp[-3].string), true))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1768 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 69: -#line 169 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyvsp[0].components).push_front(ObjectIdentifier::Component::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); } -#line 1765 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 173 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::MapComponent((yyvsp[-3].string))); (yyval.components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-5].string))); (yyval.components) = (yyvsp[0].components); } +#line 1774 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 70: -#line 172 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); } -#line 1771 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 174 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyvsp[0].components).push_front(ObjectIdentifier::SimpleComponent((yyvsp[-2].string))); (yyval.components) = (yyvsp[0].components); } +#line 1780 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 71: -#line 173 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string)); } -#line 1777 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 177 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); } +#line 1786 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 72: -#line 176 "ExpressionParser.y" /* yacc.c:1646 */ - { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); } -#line 1783 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 178 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string)); } +#line 1792 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; case 73: -#line 177 "ExpressionParser.y" /* yacc.c:1646 */ +#line 181 "ExpressionParser.y" /* yacc.c:1646 */ + { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); } +#line 1798 "ExpressionParser.tab.c" /* yacc.c:1646 */ + break; + + case 74: +#line 182 "ExpressionParser.y" /* yacc.c:1646 */ { (yyval.string_or_identifier) = ObjectIdentifier::String((yyvsp[0].string), true); } -#line 1789 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1804 "ExpressionParser.tab.c" /* yacc.c:1646 */ break; -#line 1793 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#line 1808 "ExpressionParser.tab.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -2017,5 +2041,5 @@ yyparse (void) #endif return yyresult; } -#line 180 "ExpressionParser.y" /* yacc.c:1906 */ +#line 185 "ExpressionParser.y" /* yacc.c:1906 */ diff --git a/src/App/ExpressionParser.y b/src/App/ExpressionParser.y index 3982753c755b..6ad9635d645f 100644 --- a/src/App/ExpressionParser.y +++ b/src/App/ExpressionParser.y @@ -122,22 +122,32 @@ unit_exp: UNIT { $$ = new UnitExpression(Docume identifier: path { /* Path to property within document object */ $$ = ObjectIdentifier(DocumentObject); $$.addComponents($1); + $$.resolveAmbiguity(); + } + | '.' path { /* Path to property of the current document object */ + $$ = ObjectIdentifier(DocumentObject,true); + $$.setDocumentObjectName(DocumentObject); + $$.addComponents($2); } | object '.' path { /* Path to property within document object */ $$ = ObjectIdentifier(DocumentObject); - $$.setDocumentObjectName($1, true); + $1.checkImport(DocumentObject); + $$.addComponent(ObjectIdentifier::SimpleComponent($1)); $$.addComponents($3); + $$.resolveAmbiguity(); } | document '#' path { /* Path to property from an external document, within a named document object */ $$ = ObjectIdentifier(DocumentObject); - $$.setDocumentName($1, true); + $$.setDocumentName(std::move($1), true); $$.addComponents($3); + $$.resolveAmbiguity(); } | document '#' object '.' path { /* Path to property from an external document, within a named document object */ $$ = ObjectIdentifier(DocumentObject); - $$.setDocumentName($1, true); - $$.setDocumentObjectName($3, true); + $$.setDocumentName(std::move($1), true); + $$.setDocumentObjectName(std::move($3), true); $$.addComponents($5); + $$.resolveAmbiguity(); } ; @@ -146,27 +156,27 @@ integer: INTEGER { $$ = $1; } ; -path: IDENTIFIER { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); } - | CELLADDRESS { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); } - | IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); } - | IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); $$ = $6; } - | IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); } - | IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); } - | IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); $$ = $6; } - | IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); $$ = $6; } - | IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::Component::SimpleComponent($1)); $$ = $3; } +path: IDENTIFIER { $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | CELLADDRESS { $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); } + | IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; } + | IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); } + | IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); } + | IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; } + | IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1); $$ = $6; } + | IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $3; } ; -subpath: IDENTIFIER { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); } - | STRING { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); } - | CELLADDRESS { $$.push_front(ObjectIdentifier::Component::SimpleComponent($1)); } - | IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); } - | IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::ArrayComponent($1, $3)); $$ = $6; } - | IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); } - | IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); } - | IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, ObjectIdentifier::String($3, true))); $$ = $6; } - | IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::Component::MapComponent($1, $3)); $$ = $6; } - | IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::Component::SimpleComponent($1)); $$ = $3; } +subpath: IDENTIFIER { $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | STRING { $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | CELLADDRESS { $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | IDENTIFIER '[' integer ']' { $$.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | IDENTIFIER '[' integer ']' '.' subpath { $6.push_front(ObjectIdentifier::ArrayComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; } + | IDENTIFIER '[' STRING ']' { $$.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | IDENTIFIER '[' IDENTIFIER ']' { $$.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1)); } + | IDENTIFIER '[' STRING ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent(ObjectIdentifier::String($3, true))); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; } + | IDENTIFIER '[' IDENTIFIER ']' '.' subpath { $6.push_front(ObjectIdentifier::MapComponent($3)); $$.push_front(ObjectIdentifier::SimpleComponent($1));$$ = $6; } + | IDENTIFIER '.' subpath { $3.push_front(ObjectIdentifier::SimpleComponent($1)); $$ = $3; } ; document: STRING { $$ = ObjectIdentifier::String($1, true); } diff --git a/src/App/ExpressionVisitors.h b/src/App/ExpressionVisitors.h index 00f2e9289f88..7eb290fa55b5 100644 --- a/src/App/ExpressionVisitors.h +++ b/src/App/ExpressionVisitors.h @@ -43,18 +43,8 @@ template class RenameObjectIdentifierExpressionVisitor : public Express { } - void visit(Expression *node) { - VariableExpression *expr = Base::freecad_dynamic_cast(node); - - if (expr) { - const App::ObjectIdentifier & oldPath = expr->getPath().canonicalPath(); - const std::map::const_iterator it = paths.find(oldPath); - - if (it != paths.end()) { - ExpressionModifier

::setExpressionChanged(); - expr->setPath(it->second.relativeTo(owner)); - } - } + void visit(Expression &node) { + this->renameObjectIdentifier(node,paths,owner); } @@ -63,64 +53,72 @@ template class RenameObjectIdentifierExpressionVisitor : public Express const ObjectIdentifier owner; /**< Owner of expression */ }; -/** - * @brief The RelabelDocumentObjectExpressionVisitor class is a functor class used to rename variables in an expression. - */ - -template class RelabelDocumentObjectExpressionVisitor : public ExpressionModifier

{ +template class UpdateElementReferenceExpressionVisitor : public ExpressionModifier

{ public: - RelabelDocumentObjectExpressionVisitor(P & _prop, const std::string & _oldName, const std::string & _newName) - : ExpressionModifier

(_prop) - , oldName(_oldName) - , newName(_newName) + UpdateElementReferenceExpressionVisitor(P & _prop, App::DocumentObject *feature=0, bool reverse=false) + : ExpressionModifier

(_prop),feature(feature),reverse(reverse) { } - ~RelabelDocumentObjectExpressionVisitor() { + void visit(Expression &node) { + this->updateElementReference(node,feature,reverse); } - /** - * @brief Visit each node in the expression, and if it is a VariableExpression object, incoke renameDocumentObject in it. - * @param node Node to visit - */ +private: + App::DocumentObject *feature; + bool reverse; +}; - void visit(Expression * node) { - VariableExpression *expr = Base::freecad_dynamic_cast(node); +// Document relabel is not undoable, so we don't derive from +// ExpressionModifier, hence not calling aboutToSetValue/hasSetValue(). +// By right, modification of document label should not change evaluation result +// of any expression. +class RelabelDocumentExpressionVisitor : public ExpressionVisitor { +public: - if (expr && expr->validDocumentObjectRename(oldName, newName)) { - ExpressionModifier

::setExpressionChanged(); - expr->renameDocumentObject(oldName, newName); - } + RelabelDocumentExpressionVisitor(const App::Document &doc) + : doc(doc) + { + } + + void visit(Expression &node) { + this->relabeledDocument(node,doc.getOldLabel(),doc.Label.getStrValue()); } private: - std::string oldName; /**< Document object name to replace */ - std::string newName; /**< New document object name */ + const App::Document &doc; }; -template class RelabelDocumentExpressionVisitor : public ExpressionModifier

{ +template class MoveCellsExpressionVisitor : public ExpressionModifier

{ public: + MoveCellsExpressionVisitor(P &prop, const CellAddress &address, int rowCount, int colCount) + : ExpressionModifier

(prop),address(address),rowCount(rowCount),colCount(colCount) + {} - RelabelDocumentExpressionVisitor(P & prop, const std::string & _oldName, const std::string & _newName) - : ExpressionModifier

(prop) - , oldName(_oldName) - , newName(_newName) - { + void visit(Expression &node) { + this->moveCells(node,address,rowCount,colCount); } - void visit(Expression * node) { - VariableExpression *expr = Base::freecad_dynamic_cast(node); +private: + CellAddress address; + int rowCount; + int colCount; +}; + +template class OffsetCellsExpressionVisitor : public ExpressionModifier

{ +public: + OffsetCellsExpressionVisitor(P &prop, int rowOffset, int colOffset) + : ExpressionModifier

(prop),rowOffset(rowOffset),colOffset(colOffset) + {} - if (expr && expr->validDocumentRename(oldName, newName)) { - ExpressionModifier

::setExpressionChanged(); - expr->renameDocument(oldName, newName); - } + void visit(Expression &node) { + this->offsetCells(node,rowOffset,colOffset); } private: - std::string oldName; - std::string newName; + int rowOffset; + int colOffset; }; } diff --git a/src/App/ObjectIdentifier.cpp b/src/App/ObjectIdentifier.cpp index 18c1ad1ba20a..3807c4b1d447 100644 --- a/src/App/ObjectIdentifier.cpp +++ b/src/App/ObjectIdentifier.cpp @@ -29,7 +29,11 @@ #include #include +#include + /// Here the FreeCAD includes sorted by Base,App,Gui...... +#include +#include #include "Property.h" #include "Application.h" #include "Document.h" @@ -39,21 +43,14 @@ #include #include #include +#include +#include + +FC_LOG_LEVEL_INIT("Expression",true,true) using namespace App; using namespace Base; -/** - * @brief Compute a hash value for the object identifier given by \a path. - * @param path Inputn path - * @return Hash value - */ - -std::size_t App::hash_value(const App::ObjectIdentifier & path) -{ - return boost::hash_value(path.toString()); -} - // Path class /** @@ -64,14 +61,14 @@ std::size_t App::hash_value(const App::ObjectIdentifier & path) * @return */ -std::string App::quote(const std::string &input) +std::string App::quote(const std::string &input, bool toPython) { std::stringstream output; std::string::const_iterator cur = input.begin(); std::string::const_iterator end = input.end(); - output << "<<"; + output << (toPython?"'":"<<"); while (cur != end) { switch (*cur) { case '\t': @@ -93,43 +90,58 @@ std::string App::quote(const std::string &input) output << "\\\""; break; case '>': - output << "\\>"; + output << (toPython?">":"\\>"); break; default: output << *cur; } ++cur; } - output << ">>"; + output << (toPython?"'":">>"); return output.str(); } + /** * @brief Construct an ObjectIdentifier object, given an owner and a single-value property. * @param _owner Owner of property. * @param property Name of property. */ -ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner, const std::string & property) - : owner(_owner) +ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner, + const std::string & property, int index) + : owner(0) , documentNameSet(false) , documentObjectNameSet(false) + , localProperty(false) { - if (owner) { - const DocumentObject * docObj = freecad_dynamic_cast(owner); + if (_owner) { + const DocumentObject * docObj = freecad_dynamic_cast(_owner); if (!docObj) - throw Base::RuntimeError("Property must be owned by a document object."); + FC_THROWM(Base::RuntimeError,"Property must be owned by a document object."); + owner = const_cast(docObj); if (property.size() > 0) { - const Document * doc = docObj->getDocument(); - - documentName = String(doc->getName(), false, true); - documentObjectName = String(docObj->getNameInDocument(), false, true); + setDocumentObjectName(docObj); } } - if (property.size() > 0) - addComponent(Component::SimpleComponent(property)); + if (property.size() > 0) { + addComponent(SimpleComponent(property)); + if(index!=INT_MAX) + addComponent(ArrayComponent(index)); + } +} + +ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner, bool localProperty) + :localProperty(localProperty) +{ + if (_owner) { + const DocumentObject * docObj = freecad_dynamic_cast(_owner); + if (!docObj) + FC_THROWM(Base::RuntimeError,"Property must be owned by a document object."); + owner = const_cast(docObj); + } } /** @@ -137,22 +149,24 @@ ObjectIdentifier::ObjectIdentifier(const App::PropertyContainer * _owner, const * @param prop Property to construct object identifier for. */ -ObjectIdentifier::ObjectIdentifier(const Property &prop) - : owner(prop.getContainer()) +ObjectIdentifier::ObjectIdentifier(const Property &prop, int index) + : owner(0) , documentNameSet(false) , documentObjectNameSet(false) + , localProperty(false) { DocumentObject * docObj = freecad_dynamic_cast(prop.getContainer()); if (!docObj) - throw Base::TypeError("Property must be owned by a document object."); + FC_THROWM(Base::TypeError,"Property must be owned by a document object."); - Document * doc = docObj->getDocument(); + owner = const_cast(docObj); - documentName = String(doc->getName(), false, true); - documentObjectName = String(docObj->getNameInDocument(), false, true); + setDocumentObjectName(docObj); - addComponent(Component::SimpleComponent(String(owner->getPropertyName(&prop)))); + addComponent(SimpleComponent(String(prop.getName()))); + if(index!=INT_MAX) + addComponent(ArrayComponent(index)); } /** @@ -160,13 +174,13 @@ ObjectIdentifier::ObjectIdentifier(const Property &prop) * @return Name */ -const std::string App::ObjectIdentifier::getPropertyName() const +std::string App::ObjectIdentifier::getPropertyName() const { ResolveResults result(*this); assert(result.propertyIndex >=0 && static_cast(result.propertyIndex) < components.size()); - return components[result.propertyIndex].toString(); + return components[result.propertyIndex].getName(); } /** @@ -184,6 +198,25 @@ const App::ObjectIdentifier::Component &App::ObjectIdentifier::getPropertyCompon return components[result.propertyIndex + i]; } +App::ObjectIdentifier::Component &App::ObjectIdentifier::getPropertyComponent(int i) +{ + ResolveResults result(*this); + assert(result.propertyIndex + i >=0 && + static_cast(result.propertyIndex) + i < components.size()); + return components[result.propertyIndex + i]; +} + +std::vector ObjectIdentifier::getPropertyComponents() const { + if(components.size()<=1 || documentObjectName.getString().empty()) + return components; + ResolveResults result(*this); + if(result.propertyIndex==0) + return components; + std::vector res; + res.insert(res.end(),components.begin()+result.propertyIndex,components.end()); + return res; +} + /** * @brief Compare object identifier with \a other. * @param other Other object identifier. @@ -192,18 +225,7 @@ const App::ObjectIdentifier::Component &App::ObjectIdentifier::getPropertyCompon bool ObjectIdentifier::operator ==(const ObjectIdentifier &other) const { - ResolveResults result1(*this); - ResolveResults result2(other); - - if (owner != other.owner) - return false; - if (result1.resolvedDocumentName != result2.resolvedDocumentName) - return false; - if (result1.resolvedDocumentObjectName != result2.resolvedDocumentObjectName) - return false; - if (components != other.components) - return false; - return true; + return owner==other.owner && toString() == other.toString(); } /** @@ -225,50 +247,11 @@ bool ObjectIdentifier::operator !=(const ObjectIdentifier &other) const bool ObjectIdentifier::operator <(const ObjectIdentifier &other) const { - ResolveResults result1(*this); - ResolveResults result2(other); - - if (result1.resolvedDocumentName < result2.resolvedDocumentName) + if(owner < other.owner) return true; - - if (result1.resolvedDocumentName > result2.resolvedDocumentName) + if(owner > other.owner) return false; - - if (result1.resolvedDocumentObjectName < result2.resolvedDocumentObjectName) - return true; - - if (result1.resolvedDocumentObjectName > result2.resolvedDocumentObjectName) - return false; - - if (components.size() < other.components.size()) - return true; - - if (components.size() > other.components.size()) - return false; - - for (std::size_t i = 0; i < components.size(); ++i) { - if (components[i].name < other.components[i].name) - return true; - if (components[i].name > other.components[i].name) - return false; - if (components[i].type < other.components[i].type) - return true; - if (components[i].type > other.components[i].type) - return false; - if (components[i].isArray()) { - if (components[i].index < other.components[i].index) - return true; - if (components[i].index > other.components[i].index) - return false; - } - else if (components[i].isMap()) { - if (components[i].key < other.components[i].key) - return true; - if (components[i].key > other.components[i].key) - return false; - } - } - return false; + return toString() < other.toString(); } /** @@ -293,6 +276,28 @@ int ObjectIdentifier::numSubComponents() const return components.size() - result.propertyIndex; } +bool ObjectIdentifier::verify(const App::Property &prop, bool silent) const { + ResolveResults result(*this); + if(components.size() - result.propertyIndex != 1) { + if(silent) return false; + FC_THROWM(Base::ValueError,"Invalid property path: single component expected"); + } + if(!components[result.propertyIndex].isSimple()) { + if(silent) return false; + FC_THROWM(Base::ValueError,"Invalid property path: simple component expected"); + } + const std::string &name = components[result.propertyIndex].getName(); + CellAddress addr; + bool isAddress = addr.parseAbsoluteAddress(name.c_str()); + if((isAddress && addr.toString(true) != prop.getName()) || + (!isAddress && name!=prop.getName())) + { + if(silent) return false; + FC_THROWM(Base::ValueError,"Invalid property path: name mismatch"); + } + return true; +} + /** * @brief Create a string representation of this object identifier. * @@ -303,24 +308,143 @@ int ObjectIdentifier::numSubComponents() const * @return A string */ -std::string ObjectIdentifier::toString() const +const std::string &ObjectIdentifier::toString() const { - std::stringstream s; + if(_cache.size() || !owner) + return _cache; + + std::ostringstream s; ResolveResults result(*this); - if (documentNameSet) - s << documentName.toString() << "#"; + if(result.propertyIndex >= (int)components.size()) + return _cache; + + if(localProperty || + (result.resolvedProperty && + result.resolvedDocumentObject==owner && + components.size()>1 && + components[1].isSimple() && + result.propertyIndex==0)) + { + s << '.'; + }else if (documentNameSet && documentName.getString().size()) { + if(documentObjectNameSet && documentObjectName.getString().size()) + s << documentName.toString() << "#" + << documentObjectName.toString() << '.'; + else if(result.resolvedDocumentObjectName.getString().size()) + s << documentName.toString() << "#" + << result.resolvedDocumentObjectName.toString() << '.'; + } else if (documentObjectNameSet && documentObjectName.getString().size()) { + s << documentObjectName.toString() << '.'; + } else if (result.propertyIndex > 0) { + components[0].toString(s); + s << '.'; + } + + if(subObjectName.getString().size()) + s << subObjectName.toString() << '.'; + + s << components[result.propertyIndex].getName(); + getSubPathStr(s,result); + const_cast(this)->_cache = s.str(); + return _cache; +} + +std::string ObjectIdentifier::toPersistentString() const { - if (documentObjectNameSet) - s << documentObjectName.toString() << "."; - else if (result.propertyIndex > 0) - s << components[0].toString() << "."; + if(!owner) + return std::string(); - s << getPropertyName() << getSubPathStr(); + std::ostringstream s; + ResolveResults result(*this); + if(result.propertyIndex >= (int)components.size()) + return std::string(); + + if(localProperty || + (result.resolvedProperty && + result.resolvedDocumentObject==owner && + components.size()>1 && + components[1].isSimple() && + result.propertyIndex==0)) + { + s << '.'; + }else if(result.resolvedDocumentObject && + result.resolvedDocumentObject!=owner && + result.resolvedDocumentObject->isExporting()) + { + s << result.resolvedDocumentObject->getExportName(true); + if(documentObjectName.isRealString()) + s << '@'; + s << '.'; + } else if (documentNameSet && documentName.getString().size()) { + if(documentObjectNameSet && documentObjectName.getString().size()) + s << documentName.toString() << "#" + << documentObjectName.toString() << '.'; + else if(result.resolvedDocumentObjectName.getString().size()) + s << documentName.toString() << "#" + << result.resolvedDocumentObjectName.toString() << '.'; + } else if (documentObjectNameSet && documentObjectName.getString().size()) { + s << documentObjectName.toString() << '.'; + } else if (result.propertyIndex > 0) { + components[0].toString(s); + s << '.'; + } + + if(subObjectName.getString().size()) { + const char *subname = subObjectName.getString().c_str(); + std::string exportName; + s << String(PropertyLinkBase::exportSubName(exportName, + result.resolvedDocumentObject,subname),true).toString() << '.'; + } + + s << components[result.propertyIndex].getName(); + getSubPathStr(s,result); return s.str(); } +std::size_t ObjectIdentifier::hash() const +{ + if(_hash && _cache.size()) + return _hash; + const_cast(this)->_hash = boost::hash_value(toString()); + return _hash; +} + +bool ObjectIdentifier::replaceObject(ObjectIdentifier &res, const App::DocumentObject *parent, + App::DocumentObject *oldObj, App::DocumentObject *newObj) const +{ + ResolveResults result(*this); + + if(!result.resolvedDocumentObject) + return false; + + auto r = PropertyLinkBase::tryReplaceLink(owner, result.resolvedDocumentObject, + parent, oldObj, newObj, subObjectName.getString().c_str()); + + if(!r.first) + return false; + + res = *this; + if(r.first != result.resolvedDocumentObject) { + if(r.first->getDocument()!=owner->getDocument()) { + auto doc = r.first->getDocument(); + bool useLabel = res.documentName.isRealString(); + const char *name = useLabel?doc->Label.getValue():doc->getName(); + res.setDocumentName(String(name, useLabel), true); + } + if(documentObjectName.isRealString()) + res.documentObjectName = String(r.first->Label.getValue(),true); + else + res.documentObjectName = String(r.first->getNameInDocument(),false,true); + } + res.subObjectName = String(r.second,true); + res._cache.clear(); + res.shadowSub.first.clear(); + res.shadowSub.second.clear(); + return true; +} + /** * @brief Escape toString representation so it is suitable for being embedded in a python command. * @return Escaped string. @@ -331,139 +455,81 @@ std::string ObjectIdentifier::toEscapedString() const return Base::Tools::escapedUnicodeFromUtf8(toString().c_str()); } -/** - * @brief Modify object identifier given that document object \a oldName gets the new name \a newName. - * @param oldName Name of current document object - * @param newName New name of document object - */ - -bool ObjectIdentifier::renameDocumentObject(const std::string &oldName, const std::string &newName) +bool ObjectIdentifier::updateLabelReference( + App::DocumentObject *obj, const std::string &ref, const char *newLabel) { - if (oldName == newName) + if(!owner) return false; - if (documentObjectNameSet && documentObjectName == oldName) { - if (ExpressionParser::isTokenAnIndentifier(newName)) - documentObjectName = newName; - else - documentObjectName = ObjectIdentifier::String(newName, true); - - return true; - } - else { - ResolveResults result(*this); - - if (result.propertyIndex == 1 && result.resolvedDocumentObjectName == oldName) { - if (ExpressionParser::isTokenAnIndentifier(newName)) - components[0].name = newName; - else - components[0].name = ObjectIdentifier::String(newName, true); + ResolveResults result(*this); + if(subObjectName.getString().size() && result.resolvedDocumentObject) { + std::string sub = PropertyLinkBase::updateLabelReference( + result.resolvedDocumentObject, subObjectName.getString().c_str(), obj,ref,newLabel); + if(sub.size()) { + subObjectName = String(sub,true); + _cache.clear(); return true; } } - // If object identifier uses the label then resolving the document object will fail. - // So, it must be checked if using the new label will succeed - if (!components.empty() && components[0].getName() == oldName) { - ObjectIdentifier id(*this); - id.components[0].name = newName; - - ResolveResults result(id); - - if (result.propertyIndex == 1 && result.resolvedDocumentObjectName == newName) { - if (ExpressionParser::isTokenAnIndentifier(newName)) - components[0].name = newName; - else - components[0].name = ObjectIdentifier::String(newName, true); + if(result.resolvedDocument != obj->getDocument()) + return false; - return true; - } - } - return false; -} + if(documentObjectName.getString().size()) { + if(documentObjectName.isForceIdentifier()) + return false; + if(!documentObjectName.isRealString() && + documentObjectName.getString()==obj->getNameInDocument()) + return false; -/** - * @brief Check whether a rename call with the same arguments would actually cause a rename. - * @param oldName Name of current document object - * @param newName New name of document object - */ + if(documentObjectName.getString()!=obj->Label.getValue()) + return false; -bool ObjectIdentifier::validDocumentObjectRename(const std::string &oldName, const std::string &newName) -{ - if (oldName == newName) - return false; + documentObjectName = ObjectIdentifier::String(newLabel, true); - if (documentObjectNameSet && documentObjectName == oldName) + _cache.clear(); return true; - else { - ResolveResults result(*this); + } - if (result.propertyIndex == 1 && result.resolvedDocumentObjectName == oldName) - return true; + if (result.resolvedDocumentObject==obj && + result.propertyIndex == 1 && + result.resolvedDocumentObjectName.isRealString() && + result.resolvedDocumentObjectName.getString()==obj->Label.getValue()) + { + components[0].name = ObjectIdentifier::String(newLabel, true); + _cache.clear(); + return true; } // If object identifier uses the label then resolving the document object will fail. // So, it must be checked if using the new label will succeed - if (!components.empty() && components[0].getName() == oldName) { + if (components.size()>1 && components[0].getName()==obj->Label.getValue()) { ObjectIdentifier id(*this); - id.components[0].name = newName; + id.components[0].name.str = newLabel; ResolveResults result(id); - if (result.propertyIndex == 1 && result.resolvedDocumentObjectName == newName) - return true; - } - return false; -} - -/** - * @brief Modify object identifier given that the document \a oldName has changed name to \a newName. - * @param oldName Name of current document - * @param newName New name of document - */ - -bool ObjectIdentifier::renameDocument(const std::string &oldName, const std::string &newName) -{ - if (oldName == newName) - return false; - - if (documentNameSet && documentName == oldName) { - documentName = newName; - return true; - } - else { - ResolveResults result(*this); - - if (result.resolvedDocumentName == oldName) { - documentName = newName; + if (result.propertyIndex == 1 && result.resolvedDocumentObject == obj) { + components[0].name = id.components[0].name; + _cache.clear(); return true; } } + return false; } -/** - * @brief Check whether a rename call with the same arguments would actually cause a rename. - * @param oldName Name of current document - * @param newName New name of document - */ - -bool ObjectIdentifier::validDocumentRename(const std::string &oldName, const std::string &newName) +bool ObjectIdentifier::relabeledDocument(ExpressionVisitor &v, + const std::string &oldLabel, const std::string &newLabel) { - if (oldName == newName) - return false; - - if (documentNameSet && documentName == oldName) + if (documentNameSet && documentName.isRealString() && documentName.getString()==oldLabel) { + v.aboutToChange(); + documentName = String(newLabel,true); + _cache.clear(); return true; - else { - ResolveResults result(*this); - - if (result.resolvedDocumentName == oldName) - return true; } - return false; } @@ -472,37 +538,132 @@ bool ObjectIdentifier::validDocumentRename(const std::string &oldName, const std * @return String representation of path. */ -std::string ObjectIdentifier::getSubPathStr() const +void ObjectIdentifier::getSubPathStr(std::ostream &s, const ResolveResults &result, bool toPython) const { - ResolveResults result(*this); - - std::stringstream s; std::vector::const_iterator i = components.begin() + result.propertyIndex + 1; while (i != components.end()) { - s << "." << i->toString(); + if(i->isSimple()) + s << '.'; + i->toString(s,toPython); ++i; } +} - return s.str(); +std::string ObjectIdentifier::getSubPathStr(bool toPython) const { + std::ostringstream ss; + getSubPathStr(ss,ResolveResults(*this),toPython); + return ss.str(); } + /** * @brief Construct a Component part - * @param _component Name of component - * @param _type Type; simple, array, or map - * @param _index Array index, if type is array, or -1 if simple or map. - * @param _key Key index, if type is map, ir empty if simple or array. + * @param _name Name of component + * @param _type Type; simple, array, range or map + * @param _begin Array index or begining of a Range, or INT_MAX for other type. + * @param _end ending of a Range, or INT_MAX for other type. */ -ObjectIdentifier::Component::Component(const String &_component, ObjectIdentifier::Component::typeEnum _type, int _index, String _key) - : name(_component) +ObjectIdentifier::Component::Component(const String &_name, + ObjectIdentifier::Component::typeEnum _type, int _begin, int _end, int _step) + : name(_name) , type(_type) - , index(_index) - , key(_key) - , keyIsString(false) + , begin(_begin) + , end(_end) + , step(_step) { } +ObjectIdentifier::Component::Component(String &&_name, + ObjectIdentifier::Component::typeEnum _type, int _begin, int _end, int _step) + : name(std::move(_name)) + , type(_type) + , begin(_begin) + , end(_end) + , step(_step) +{ +} + + +size_t ObjectIdentifier::Component::getIndex(size_t count) const { + if(begin>=0) { + if(begin<(int)count) + return begin; + }else { + int idx = begin + (int)count; + if(idx >= 0) + return idx; + } + FC_THROWM(Base::IndexError, "Array out of bound: " << begin << ", " << count); +} + +Py::Object ObjectIdentifier::Component::get(const Py::Object &pyobj) const { + Py::Object res; + if(isSimple()) { + res = pyobj.getAttr(getName()); + } else if(isArray()) { + if(pyobj.isMapping()) + res = Py::Mapping(pyobj).getItem(Py::Int(begin)); + else + res = Py::Sequence(pyobj).getItem(begin); + }else if(isMap()) + res = Py::Mapping(pyobj).getItem(getName()); + else { + assert(isRange()); + Py::Object slice(PySlice_New(Py::Int(begin).ptr(), + end!=INT_MAX?Py::Int(end).ptr():0, + step!=1?Py::Int(step).ptr():0)); + PyObject *r = PyObject_GetItem(pyobj.ptr(),slice.ptr()); + if(!r) + Base::PyException::ThrowException(); + res = Py::asObject(r); + } + if(PyModule_Check(res.ptr()) && !ExpressionParser::isModuleImported(res.ptr())) + FC_THROWM(Base::RuntimeError, "Module '" << getName() << "' access denied."); + return res; +} + +void ObjectIdentifier::Component::set(Py::Object &pyobj, const Py::Object &value) const { + if(isSimple()) { + if(PyObject_SetAttrString(*pyobj, getName().c_str(), *value ) == -1) + Base::PyException::ThrowException(); + } else if(isArray()) { + if(pyobj.isMapping()) + Py::Mapping(pyobj).setItem(Py::Int(begin),value); + else + Py::Sequence(pyobj).setItem(begin,value); + }else if(isMap()) + Py::Mapping(pyobj).setItem(getName(),value); + else { + assert(isRange()); + Py::Object slice(PySlice_New(Py::Int(begin).ptr(), + end!=INT_MAX?Py::Int(end).ptr():0, + step!=1?Py::Int(step).ptr():0)); + if(PyObject_SetItem(pyobj.ptr(),slice.ptr(),value.ptr())<0) + Base::PyException::ThrowException(); + } +} + +void ObjectIdentifier::Component::del(Py::Object &pyobj) const { + if(isSimple()) + pyobj.delAttr(getName()); + else if(isArray()) { + if(pyobj.isMapping()) + Py::Mapping(pyobj).delItem(Py::Int(begin)); + else + PySequence_DelItem(pyobj.ptr(),begin); + } else if(isMap()) + Py::Mapping(pyobj).delItem(getName()); + else { + assert(isRange()); + Py::Object slice(PySlice_New(Py::Int(begin).ptr(), + end!=INT_MAX?Py::Int(end).ptr():0, + step!=1?Py::Int(step).ptr():0)); + if(PyObject_DelItem(pyobj.ptr(),slice.ptr())<0) + Base::PyException::ThrowException(); + } +} + /** * @brief Create a simple component part with the given name * @param _component Name of component. @@ -525,6 +686,11 @@ ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(const O return Component(_component); } +ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String &&_component) +{ + return Component(std::move(_component)); +} + /** * @brief Create an array component with given name and index. * @param _component Name of component @@ -532,9 +698,9 @@ ObjectIdentifier::Component ObjectIdentifier::Component::SimpleComponent(const O * @return A new Component object. */ -ObjectIdentifier::Component ObjectIdentifier::Component::ArrayComponent(const ObjectIdentifier::String &_component, int _index) +ObjectIdentifier::Component ObjectIdentifier::Component::ArrayComponent(int _index) { - return Component(_component, ARRAY, _index); + return Component(String(), Component::ARRAY, _index); } /** @@ -544,9 +710,27 @@ ObjectIdentifier::Component ObjectIdentifier::Component::ArrayComponent(const Ob * @return A new Component object. */ -ObjectIdentifier::Component ObjectIdentifier::Component::MapComponent(const ObjectIdentifier::String &_component, const String & _key) +ObjectIdentifier::Component ObjectIdentifier::Component::MapComponent(const String & _key) { - return Component(_component, MAP, -1, _key); + return Component(_key, Component::MAP); +} + +ObjectIdentifier::Component ObjectIdentifier::Component::MapComponent(String &&_key) +{ + return Component(std::move(_key), Component::MAP); +} + + +/** + * @brief Create a range component with given begin and end. + * @param _begin begining index of the range + * @param _end ending index of the range + * @return A new Component object. + */ + +ObjectIdentifier::Component ObjectIdentifier::Component::RangeComponent(int _begin, int _end, int _step) +{ + return Component(String(), Component::RANGE, _begin, _end, _step); } /** @@ -560,16 +744,14 @@ bool ObjectIdentifier::Component::operator ==(const ObjectIdentifier::Component if (type != other.type) return false; - if (name != other.name) - return false; - switch (type) { case SIMPLE: - return true; - case ARRAY: - return index == other.index; case MAP: - return key == other.key; + return name == other.name; + case ARRAY: + return begin == other.begin; + case RANGE: + return begin == other.begin && end == other.end && step==other.step; default: assert(0); return false; @@ -581,27 +763,39 @@ bool ObjectIdentifier::Component::operator ==(const ObjectIdentifier::Component * @return A string representing the component. */ -std::string ObjectIdentifier::Component::toString() const +void ObjectIdentifier::Component::toString(std::ostream &ss, bool toPython) const { - std::stringstream s; - - s << name.toString(); switch (type) { case Component::SIMPLE: + ss << name.getString(); break; case Component::MAP: - s << "[" << key.toString() << "]"; + ss << "[" << name.toString(toPython) << "]"; break; case Component::ARRAY: - s << "[" << index << "]"; + ss << "[" << begin << "]"; + break; + case Component::RANGE: + ss << '['; + if(begin!=INT_MAX) + ss << begin; + ss << ':'; + if(end!=INT_MAX) + ss << end; + if(step!=1) + ss << ':' << step; + ss << ']'; break; default: assert(0); } - - return s.str(); } +enum ResolveFlags { + ResolveByIdentifier, + ResolveByLabel, + ResolveAmbiguous, +}; /** * @brief Search for the document object given by name in doc. @@ -614,25 +808,32 @@ std::string ObjectIdentifier::Component::toString() const * @return Pointer to document object if a unique pointer is found, 0 otherwise. */ -App::DocumentObject * ObjectIdentifier::getDocumentObject(const App::Document * doc, const String & name, bool & byIdentifier) const +App::DocumentObject * ObjectIdentifier::getDocumentObject(const App::Document * doc, + const String & name, std::bitset<32> &flags) { DocumentObject * objectById = 0; DocumentObject * objectByLabel = 0; - std::vector docObjects = doc->getObjects(); - // No object found with matching label, try using name directly - objectById = doc->getObject(static_cast(name)); + if(!name.isRealString()) { + // No object found with matching label, try using name directly + objectById = doc->getObject(static_cast(name)); - if (name.isForceIdentifier()) { - byIdentifier = true; - return objectById; + if (objectById) { + flags.set(ResolveByIdentifier); + return objectById; + } + if(name.isForceIdentifier()) + return 0; } + std::vector docObjects = doc->getObjects(); for (std::vector::iterator j = docObjects.begin(); j != docObjects.end(); ++j) { if (strcmp((*j)->Label.getValue(), static_cast(name)) == 0) { // Found object with matching label - if (objectByLabel != 0) + if (objectByLabel != 0) { + FC_WARN("duplicate object label " << doc->getName() << '#' << name); return 0; + } objectByLabel = *j; } } @@ -640,19 +841,22 @@ App::DocumentObject * ObjectIdentifier::getDocumentObject(const App::Document * if (objectByLabel == 0 && objectById == 0) // Not found at all return 0; else if (objectByLabel == 0) { // Found by name - byIdentifier = true; + flags.set(ResolveByIdentifier); return objectById; } else if (objectById == 0) { // Found by label - byIdentifier = false; + flags.set(ResolveByLabel); return objectByLabel; } else if (objectByLabel == objectById) { // Found by both name and label, same object - byIdentifier = false; + flags.set(ResolveByIdentifier); + flags.set(ResolveByLabel); return objectByLabel; } - else + else { + flags.set(ResolveAmbiguous); return 0; // Found by both name and label, two different objects + } } /** @@ -664,28 +868,34 @@ App::DocumentObject * ObjectIdentifier::getDocumentObject(const App::Document * void ObjectIdentifier::resolve(ResolveResults &results) const { - if (freecad_dynamic_cast(owner) == 0) + if(!owner) return; + bool docAmbiguous = false; + /* Document name specified? */ if (documentName.getString().size() > 0) { - results.resolvedDocument = getDocument(documentName); + results.resolvedDocument = getDocument(documentName,&docAmbiguous); results.resolvedDocumentName = documentName; } else { - results.resolvedDocument = freecad_dynamic_cast(owner)->getDocument(); + results.resolvedDocument = owner->getDocument(); results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true); } + results.subObjectName = subObjectName; results.propertyName = ""; results.propertyIndex = 0; // Assume document name and object name from owner if not found if (results.resolvedDocument == 0) { - if (documentName.getString().size() > 0) + if (documentName.getString().size() > 0) { + if(docAmbiguous) + results.flags.set(ResolveAmbiguous); return; + } - results.resolvedDocument = freecad_dynamic_cast(owner)->getDocument(); + results.resolvedDocument = owner->getDocument(); if (results.resolvedDocument == 0) return; } @@ -694,16 +904,16 @@ void ObjectIdentifier::resolve(ResolveResults &results) const /* Document object name specified? */ if (documentObjectName.getString().size() > 0) { - bool dummy; - results.resolvedDocumentObjectName = documentObjectName; - results.resolvedDocumentObject = getDocumentObject(results.resolvedDocument, documentObjectName, dummy); + results.resolvedDocumentObject = getDocumentObject( + results.resolvedDocument, documentObjectName, results.flags); if (!results.resolvedDocumentObject) return; + if (components.size() > 0) { results.propertyName = components[0].name.getString(); results.propertyIndex = 0; - results.resolvedProperty = results.resolvedDocumentObject->getPropertyByName(results.propertyName.c_str()); + results.getProperty(*this); } else return; @@ -712,55 +922,56 @@ void ObjectIdentifier::resolve(ResolveResults &results) const /* Document object name not specified, resolve from path */ /* One component? */ - if (components.size() == 1) { + if (components.size() == 1 || (components.size()>1 && !components[0].isSimple())) { /* Yes -- then this must be a property, so we get the document object's name from the owner */ - bool byIdentifier; - - results.resolvedDocumentObjectName = String(static_cast(owner)->getNameInDocument(), false, true); - results.resolvedDocumentObject = getDocumentObject(results.resolvedDocument, results.resolvedDocumentObjectName, byIdentifier); + results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true); + results.resolvedDocumentObject = owner; results.propertyName = components[0].name.getString(); - if (results.resolvedDocumentObject) - results.resolvedProperty = results.resolvedDocumentObject->getPropertyByName(results.propertyName.c_str()); results.propertyIndex = 0; + results.getProperty(*this); } else if (components.size() >= 2) { /* No -- */ - bool byIdentifier; - if (!components[0].isSimple()) return; - results.resolvedDocumentObject = getDocumentObject(results.resolvedDocument, components[0].name, byIdentifier); + results.resolvedDocumentObject = getDocumentObject( + results.resolvedDocument, components[0].name, results.flags); /* Possible to resolve component to a document object? */ if (results.resolvedDocumentObject) { /* Yes */ - results.resolvedDocumentObjectName = String(components[0].name, false, byIdentifier); + results.resolvedDocumentObjectName = String( + components[0].name, false, results.flags.test(ResolveByIdentifier)); results.propertyName = components[1].name.getString(); - results.resolvedProperty = results.resolvedDocumentObject->getPropertyByName(results.propertyName.c_str()); results.propertyIndex = 1; - } - else { - - /* Document name set explicitly? */ - if (documentName.getString().size() > 0) { - /* Yes; then document object must follow */ - results.resolvedDocumentObjectName = String(components[0].name, false, false); - results.resolvedDocumentObject = results.resolvedDocument->getObject(static_cast(owner)->getNameInDocument()); - results.propertyIndex = 1; - } - else { - /* No, assume component is a property, and get document object's name from owner */ - const DocumentObject * docObj = static_cast(owner); - results.resolvedDocument = docObj->getDocument(); - results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true); - results.resolvedDocumentObjectName = String(docObj->getNameInDocument(), false, true); - results.resolvedDocumentObject = docObj->getDocument()->getObject(docObj->getNameInDocument()); - results.propertyIndex = 0; + results.getProperty(*this); + if(!results.resolvedProperty) { + // If the second component is not a property name, try to + // interpret the first component as the property name. + DocumentObject *sobj = 0; + results.resolvedProperty = resolveProperty( + owner,components[0].name,sobj,results.propertyType); + if(results.resolvedProperty) { + results.propertyName = components[0].name.getString(); + results.resolvedDocument = owner->getDocument(); + results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true); + results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true); + results.resolvedDocumentObject = owner; + results.resolvedSubObject = sobj; + results.propertyIndex = 0; + } } + } + else if (documentName.getString().empty()) { + /* No, assume component is a property, and get document object's name from owner */ + results.resolvedDocument = owner->getDocument(); + results.resolvedDocumentName = String(results.resolvedDocument->getName(), false, true); + results.resolvedDocumentObjectName = String(owner->getNameInDocument(), false, true); + results.resolvedDocumentObject = owner->getDocument()->getObject(owner->getNameInDocument()); + results.propertyIndex = 0; results.propertyName = components[results.propertyIndex].name.getString(); - if (results.resolvedDocumentObject) - results.resolvedProperty = results.resolvedDocumentObject->getPropertyByName(results.propertyName.c_str()); + results.getProperty(*this); } } else @@ -774,15 +985,18 @@ void ObjectIdentifier::resolve(ResolveResults &results) const * @return Pointer to document, or 0 if it is not found or not uniquely defined by name. */ -Document * ObjectIdentifier::getDocument(String name) const +Document * ObjectIdentifier::getDocument(String name, bool *ambiguous) const { if (name.getString().size() == 0) name = getDocumentName(); - App::Document * docById = App::GetApplication().getDocument(name); - - if (name.isForceIdentifier()) - return docById; + App::Document * docById = 0; + + if(!name.isRealString()) { + docById = App::GetApplication().getDocument(name); + if (name.isForceIdentifier()) + return docById; + } App::Document * docByLabel = 0; const std::vector docs = App::GetApplication().getDocuments(); @@ -790,8 +1004,10 @@ Document * ObjectIdentifier::getDocument(String name) const for (std::vector::const_iterator i = docs.begin(); i != docs.end(); ++i) { if ((*i)->Label.getValue() == name.getString()) { /* Multiple hits for same label? */ - if (docByLabel != 0) + if (docByLabel != 0) { + if(ambiguous) *ambiguous = true; return 0; + } docByLabel = *i; } } @@ -805,7 +1021,10 @@ Document * ObjectIdentifier::getDocument(String name) const return docById; /* docByLabel and docById could be equal; that is ok */ - return docByLabel == docById ? docById : 0; + if(docByLabel==docById) + return docById; + if(ambiguous) *ambiguous = true; + return 0; } } @@ -817,7 +1036,7 @@ Document * ObjectIdentifier::getDocument(String name) const DocumentObject *ObjectIdentifier::getDocumentObject() const { const App::Document * doc = getDocument(); - bool dummy; + std::bitset<32> dummy; if (!doc) return 0; @@ -827,6 +1046,53 @@ DocumentObject *ObjectIdentifier::getDocumentObject() const return getDocumentObject(doc, result.resolvedDocumentObjectName, dummy); } + +enum PseudoPropertyType { + PseudoNone, + PseudoShape, + PseudoPlacement, + PseudoMatrix, + PseudoLinkPlacement, + PseudoLinkMatrix, + PseudoSelf, + PseudoApp, + PseudoPart, + PseudoRegex, + PseudoBuiltins, + PseudoMath, + PseudoCollections, + PseudoGui, + PseudoCadquery, +}; + +std::pair ObjectIdentifier::getDep(std::vector *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(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()); + } + } + return std::make_pair(result.resolvedDocumentObject,std::string()); +} + /** * @brief Get components as a string list. * @return List of strings. @@ -837,14 +1103,21 @@ std::vector ObjectIdentifier::getStringList() const std::vector l; ResolveResults result(*this); - if (documentNameSet) - l.push_back(result.resolvedDocumentName.toString()); - if (documentObjectNameSet) - l.push_back(result.resolvedDocumentObjectName.toString()); + if(!result.resolvedProperty || result.resolvedDocumentObject != owner) { + if (documentNameSet) + l.push_back(documentName.toString()); + if (documentObjectNameSet) + l.push_back(documentObjectName.toString()); + } + if(subObjectName.getString().size()) { + l.back() += subObjectName.toString(); + } std::vector::const_iterator i = components.begin(); while (i != components.end()) { - l.push_back(i->toString()); + std::ostringstream ss; + i->toString(ss); + l.push_back(ss.str()); ++i; } @@ -864,9 +1137,10 @@ ObjectIdentifier ObjectIdentifier::relativeTo(const ObjectIdentifier &other) con ResolveResults otherresult(other); if (otherresult.resolvedDocument != thisresult.resolvedDocument) - result.setDocumentName(thisresult.resolvedDocumentName, true); + result.setDocumentName(std::move(thisresult.resolvedDocumentName), true); if (otherresult.resolvedDocumentObject != thisresult.resolvedDocumentObject) - result.setDocumentObjectName(thisresult.resolvedDocumentObjectName, true); + result.setDocumentObjectName( + std::move(thisresult.resolvedDocumentObjectName), true, String(subObjectName)); for (std::size_t i = thisresult.propertyIndex; i < components.size(); ++i) result << components[i]; @@ -892,7 +1166,7 @@ ObjectIdentifier ObjectIdentifier::parse(const DocumentObject *docObj, const std if (v) return v->getPath(); else - throw Base::RuntimeError("Invalid property specification."); + FC_THROWM(Base::RuntimeError,"Invalid property specification."); } std::string ObjectIdentifier::resolveErrorString() const @@ -911,21 +1185,91 @@ std::string ObjectIdentifier::resolveErrorString() const ObjectIdentifier &ObjectIdentifier::operator <<(const ObjectIdentifier::Component &value) { components.push_back(value); + _cache.clear(); + return *this; +} + +ObjectIdentifier &ObjectIdentifier::operator <<(ObjectIdentifier::Component &&value) +{ + components.push_back(std::move(value)); + _cache.clear(); return *this; } + /** * @brief Get pointer to property pointed to by this object identifier. * @return Point to property if it is uniquely defined, or 0 otherwise. */ -Property *ObjectIdentifier::getProperty() const +Property *ObjectIdentifier::getProperty(int *ptype) const { ResolveResults result(*this); - + if(ptype) + *ptype = result.propertyType; return result.resolvedProperty; } +Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj, + const char *propertyName, App::DocumentObject *&sobj, int &ptype) const +{ + if(obj && subObjectName.getString().size()) { + sobj = obj->getSubObject(subObjectName); + obj = sobj; + } + if(!obj) + return 0; + + static std::map _props = { + {"_shape",PseudoShape}, + {"_pla",PseudoPlacement}, + {"_matrix",PseudoMatrix}, + {"__pla",PseudoLinkPlacement}, + {"__matrix",PseudoLinkMatrix}, + {"_self",PseudoSelf}, + {"_app",PseudoApp}, + {"_part",PseudoPart}, + {"_re",PseudoRegex}, + {"_py", PseudoBuiltins}, + {"_math", PseudoMath}, + {"_coll", PseudoCollections}, + {"_gui",PseudoGui}, + {"_cq",PseudoCadquery}, + }; + auto it = _props.find(propertyName); + if(it == _props.end()) + ptype = PseudoNone; + else { + ptype = it->second; + if(ptype != PseudoShape && + subObjectName.getString().size() && + !boost::ends_with(subObjectName.getString(),".")) + { + return 0; + } + return &const_cast(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(true); + if(!ext) + return prop; + linked = ext->getTrueLinkedObject(true); + if(!linked || linked==obj) + return prop; + } + + auto linkedProp = linked->getPropertyByName(propertyName); + return linkedProp?linkedProp:prop; +} + + + /** * @brief Create a canonical representation of an object identifier. * @@ -937,19 +1281,28 @@ Property *ObjectIdentifier::getProperty() const ObjectIdentifier ObjectIdentifier::canonicalPath() const { - // Simplify input path by ensuring that components array only has property + optional sub-properties first. - ObjectIdentifier simplified(getDocumentObject()); - - ResolveResults result(*this); - - for (std::size_t i = result.propertyIndex; i < components.size(); ++i) - simplified << components[i]; - - Property * prop = getProperty(); + ObjectIdentifier res(*this); + ResolveResults result(res); + if(result.resolvedDocumentObject && result.resolvedDocumentObject!=owner) { + res.owner = result.resolvedDocumentObject; + res._cache.clear(); + } + res.resolveAmbiguity(result); + if(!result.resolvedProperty || result.propertyType!=PseudoNone) + return res; + return result.resolvedProperty->canonicalPath(res); +} - // Invoke properties canonicalPath method, to let the property do the rest of the job. +static const std::map *_DocumentMap; +ObjectIdentifier::DocumentMapper::DocumentMapper(const std::map &map) +{ + assert(!_DocumentMap); + _DocumentMap = ↦ +} - return prop ? prop->canonicalPath(simplified) : simplified; +ObjectIdentifier::DocumentMapper::~DocumentMapper() +{ + _DocumentMap = 0; } /** @@ -961,10 +1314,28 @@ ObjectIdentifier ObjectIdentifier::canonicalPath() const * @param force Force name to be set */ -void ObjectIdentifier::setDocumentName(const ObjectIdentifier::String &name, bool force) +void ObjectIdentifier::setDocumentName(ObjectIdentifier::String &&name, bool force) { - documentName = name; + if(name.getString().empty()) + force = false; documentNameSet = force; + _cache.clear(); + if(name.getString().size() && _DocumentMap) { + if(name.isRealString()) { + auto iter = _DocumentMap->find(name.toString()); + if(iter!=_DocumentMap->end()) { + documentName = String(iter->second,true); + return; + } + }else{ + auto iter = _DocumentMap->find(name.getString()); + if(iter!=_DocumentMap->end()) { + documentName = String(iter->second,false,true); + return; + } + } + } + documentName = std::move(name); } /** @@ -973,7 +1344,7 @@ void ObjectIdentifier::setDocumentName(const ObjectIdentifier::String &name, boo * @return Document name as a String object. */ -const ObjectIdentifier::String ObjectIdentifier::getDocumentName() const +ObjectIdentifier::String ObjectIdentifier::getDocumentName() const { ResolveResults result(*this); @@ -990,157 +1361,505 @@ const ObjectIdentifier::String ObjectIdentifier::getDocumentName() const * @param force Force name to be set. */ -void ObjectIdentifier::setDocumentObjectName(const ObjectIdentifier::String &name, bool force) +void ObjectIdentifier::setDocumentObjectName(ObjectIdentifier::String &&name, bool force, + ObjectIdentifier::String &&subname, bool checkImport) +{ + if(checkImport) { + name.checkImport(owner); + subname.checkImport(owner,0,&name); + } + + documentObjectName = std::move(name); + documentObjectNameSet = force; + subObjectName = std::move(subname); + + _cache.clear(); +} + +void ObjectIdentifier::setDocumentObjectName(const App::DocumentObject *obj, bool force, + ObjectIdentifier::String &&subname, bool checkImport) { - documentObjectName = name; + if(!owner || !obj || !obj->getNameInDocument() || !obj->getDocument()) + FC_THROWM(Base::RuntimeError,"invalid object"); + + if(checkImport) + subname.checkImport(owner,obj); + + if(obj == owner) + force = false; + else + localProperty = false; + if(obj->getDocument() == owner->getDocument()) + setDocumentName(String()); + else if(!documentNameSet) { + if(obj->getDocument() == owner->getDocument()) + setDocumentName(String()); + else { + documentNameSet = true; + documentName = String(obj->getDocument()->getName(),false,true); + } + }else if(documentName.isRealString()) + documentName = String(obj->getDocument()->Label.getStrValue(),true); + else + documentName = String(obj->getDocument()->getName(),false,true); + documentObjectNameSet = force; + documentObjectName = String(obj->getNameInDocument(),false,true); + subObjectName = std::move(subname); + + _cache.clear(); } + /** * @brief Get the document object name * @return String with name of document object as resolved by object identifier. */ -const ObjectIdentifier::String ObjectIdentifier::getDocumentObjectName() const +ObjectIdentifier::String ObjectIdentifier::getDocumentObjectName() const { ResolveResults result(*this); return result.resolvedDocumentObjectName; } +bool ObjectIdentifier::hasDocumentObjectName(bool forced) const { + return !documentObjectName.getString().empty() && (!forced || documentObjectNameSet); +} + /** * @brief Get a string representation of this object identifier. * @return String representation. */ -std::string ObjectIdentifier::String::toString() const +std::string ObjectIdentifier::String::toString(bool toPython) const { if (isRealString()) - return quote(str); + return quote(str,toPython); else return str; } -/** - * @brief Return a string that can be used to access the property or field pointed to by - * this object identifier. - * @return Python code as a string - */ - -std::string ObjectIdentifier::getPythonAccessor() const +void ObjectIdentifier::String::checkImport(const App::DocumentObject *owner, + const App::DocumentObject *obj, String *objName) { - std::stringstream s; - DocumentObject * docObj = getDocumentObject(); + if(owner && owner->getDocument() && + str.size() && + ExpressionParser::ExpressionImporter::reader()) + { + auto reader = ExpressionParser::ExpressionImporter::reader(); + if(obj || objName) { + bool restoreLabel = false; + str = PropertyLinkBase::importSubName(*reader,str.c_str(),restoreLabel); + if(restoreLabel) { + if(!obj) { + std::bitset<32> flags; + obj = getDocumentObject(owner->getDocument(),*objName,flags); + if(!obj) + FC_ERR("Cannot find object " << objName->toString()); + } + PropertyLinkBase::restoreLabelReference(obj,str); + } + } else if (str.back()!='@') + str = reader->getName(str.c_str()); + else{ + str.resize(str.size()-1); + auto mapped = reader->getName(str.c_str()); + auto obj = owner->getDocument()->getObject(mapped); + if(!obj) + FC_ERR("Cannot find object " << str); + else { + isString = true; + forceIdentifier = false; + str = obj->Label.getValue(); + } + } + } +} - s << "App.getDocument('" << getDocumentName() << "')." - << "getObject('" << docObj->getNameInDocument() << "')." - << getPropertyName() << getSubPathStr(); +Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *value) const +{ + if(!result.resolvedDocumentObject || !result.resolvedProperty || + (subObjectName.getString().size() && !result.resolvedSubObject)) + { + FC_THROWM(Base::RuntimeError, result.resolveErrorString() << std::endl + << "in '" << toString() << "'"); + } - return s.str(); + Py::Object pyobj; + int ptype = result.propertyType; + + // NOTE! We do not keep reference of the imported module, assuming once + // imported they'll live (because of sys.modules) till the application + // dies. +#define GET_MODULE(_name) do {\ + static PyObject *pymod;\ + if(!pymod) {\ + pymod = PyImport_ImportModule(#_name);\ + if(!pymod)\ + Base::PyException::ThrowException();\ + else\ + Py_DECREF(pymod);\ + }\ + pyobj = Py::Object(pymod);\ + }while(0) + + size_t idx = result.propertyIndex+1; + switch(ptype) { + case PseudoApp: + GET_MODULE(FreeCAD); + break; + case PseudoGui: + GET_MODULE(FreeCADGui); + break; + case PseudoPart: + GET_MODULE(Part); + break; + case PseudoCadquery: + GET_MODULE(freecad.fc_cadquery); + break; + case PseudoRegex: + GET_MODULE(re); + break; + case PseudoBuiltins: +#if PY_MAJOR_VERSION < 3 + GET_MODULE(__builtin__); +#else + GET_MODULE(builtins); +#endif + break; + case PseudoMath: + GET_MODULE(math); + break; + case PseudoCollections: + GET_MODULE(collections); + break; + case PseudoShape: { + GET_MODULE(Part); + Py::Callable func(pyobj.getAttr("getShape")); + Py::Tuple tuple(1); + tuple.setItem(0,Py::Object(result.resolvedDocumentObject->getPyObject(),true)); + if(result.subObjectName.getString().empty()) + pyobj = func.apply(tuple); + else{ + Py::Dict dict; + dict.setItem("subname",Py::String(result.subObjectName.getString())); + dict.setItem("needSubElement",Py::True()); + pyobj = func.apply(tuple,dict); + } + break; + } default: { + Base::Matrix4D mat; + auto obj = result.resolvedDocumentObject; + switch(ptype) { + case PseudoPlacement: + case PseudoMatrix: + case PseudoLinkPlacement: + case PseudoLinkMatrix: + obj->getSubObject(result.subObjectName.getString().c_str(),0,&mat); + break; + default: + break; + } + if(result.resolvedSubObject) + obj = result.resolvedSubObject; + switch(ptype) { + case PseudoPlacement: + pyobj = Py::Placement(Base::Placement(mat)); + break; + case PseudoMatrix: + pyobj = Py::Matrix(mat); + break; + case PseudoLinkPlacement: + case PseudoLinkMatrix: { + auto linked = obj->getLinkedObject(true,&mat,false); + if(!linked || linked==obj) { + auto ext = obj->getExtensionByType(true); + if(ext) + ext->getTrueLinkedObject(true,&mat); + } + if(ptype == PseudoLinkPlacement) + pyobj = Py::Placement(Base::Placement(mat)); + else + pyobj = Py::Matrix(mat); + break; + } + case PseudoSelf: + pyobj = Py::Object(obj->getPyObject(),true); + break; + default: { + // NOTE! We cannot directly call Property::getPyObject(), but + // instead, must obtain the property's python object through + // DocumentObjectPy::getAttr(). Because, PyObjectBase has internal + // attribute tracking only if we obtain attribute through + // getAttr(). Without attribute tracking, we can't do things like + // + // obj.Placement.Base.x = 10. + // + // What happens is that the when Python interpreter calls + // + // Base.setAttr('x', 10), + // + // PyObjectBase will lookup Base's parent, i.e. Placement, and call + // + // Placement.setAttr('Base', Base), + // + // and in turn calls + // + // obj.setAttr('Placement',Placement) + // + // The tracking logic is implemented in PyObjectBase::__getattro/__setattro + + auto container = result.resolvedProperty->getContainer(); + if(container + && container!=result.resolvedDocumentObject + && container!=result.resolvedSubObject) + { + if(!container->isDerivedFrom(DocumentObject::getClassTypeId())) + FC_WARN("Invalid property container"); + else + obj = static_cast(container); + } + pyobj = Py::Object(obj->getPyObject(),true); + idx = result.propertyIndex; + break; + }}} + } + if(components.empty()) + return pyobj; + size_t count = components.size(); + if(value) --count; + assert(idx<=count); + for(;idxgetPropertyByName(components[rs.propertyIndex+1].getName().c_str())) + { + *isPseudoProperty = false; + } + } - destructor d1(pyvalue); + if(rs.resolvedProperty && rs.propertyType==PseudoNone && pathValue) + return rs.resolvedProperty->getPathValue(*this); - Base::PyGILStateLocker locker; - if (!pyvalue) - throw Base::RuntimeError("Failed to get property value."); -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(pyvalue)) - return boost::any(PyInt_AsLong(pyvalue)); -#else - if (PyLong_Check(pyvalue)) - return boost::any(PyLong_AsLong(pyvalue)); -#endif - else if (PyFloat_Check(pyvalue)) - return boost::any(PyFloat_AsDouble(pyvalue)); -#if PY_MAJOR_VERSION < 3 - else if (PyString_Check(pyvalue)) - return boost::any(PyString_AsString(pyvalue)); -#endif - else if (PyUnicode_Check(pyvalue)) { -#if PY_MAJOR_VERSION >= 3 - return boost::any(PyUnicode_AsUTF8(pyvalue)); -#else - PyObject * s = PyUnicode_AsUTF8String(pyvalue); - destructor d2(s); + Base::PyGILStateLocker lock; + try { + return pyObjectToAny(access(rs)); + }catch(Py::Exception &) { + Base::PyException::ThrowException(); + } + return App::any(); +} - return boost::any(PyString_AsString(s)); -#endif +Py::Object ObjectIdentifier::getPyValue(bool pathValue, bool *isPseudoProperty) const +{ + ResolveResults rs(*this); + + if(isPseudoProperty) { + *isPseudoProperty = rs.propertyType!=PseudoNone; + if(rs.propertyType == PseudoSelf + && isLocalProperty() + && rs.propertyIndex+1 < (int)components.size() + && owner->getPropertyByName(components[rs.propertyIndex+1].getName().c_str())) + { + *isPseudoProperty = false; + } } - else if (PyObject_TypeCheck(pyvalue, &Base::QuantityPy::Type)) { - Base::QuantityPy * qp = static_cast(pyvalue); - Base::Quantity * q = qp->getQuantityPtr(); - return boost::any(*q); + if(rs.resolvedProperty && rs.propertyType==PseudoNone && pathValue) { + Py::Object res; + if(rs.resolvedProperty->getPyPathValue(*this,res)) + return res; } - else { - throw Base::TypeError("Invalid property type."); + + try { + return access(rs); + }catch(Py::Exception &) { + Base::PyException::ThrowException(); } + return Py::Object(); } /** * @brief Set value of a property or field pointed to by this object identifier. * * This method uses Python to do the actual work. and a limited set of types that - * can be in the boost::any variable is supported: Base::Quantity, double, + * can be in the App::any variable is supported: Base::Quantity, double, * char*, const char*, int, unsigned int, short, unsigned short, char, and unsigned char. * * @param value Value to set */ -void ObjectIdentifier::setValue(const boost::any &value) const +void ObjectIdentifier::setValue(const App::any &value) const { std::stringstream ss; + ResolveResults rs(*this); + if(rs.propertyType) + FC_THROWM(Base::RuntimeError,"Cannot set pseudo property"); + + Base::PyGILStateLocker lock; + try { + Py::Object pyvalue = pyObjectFromAny(value); + access(rs,&pyvalue); + }catch(Py::Exception &) { + Base::PyException::ThrowException(); + } +} + +const std::string &ObjectIdentifier::getSubObjectName(bool newStyle) const { + if(newStyle && shadowSub.first.size()) + return shadowSub.first; + if(shadowSub.second.size()) + return shadowSub.second; + return subObjectName.getString(); +} - ss << getPythonAccessor() + " = "; - - if (value.type() == typeid(Base::Quantity)) - ss << std::setprecision(std::numeric_limits::digits10 + 1) << boost::any_cast(value).getValue(); - else if (value.type() == typeid(double)) - ss << std::setprecision(std::numeric_limits::digits10 + 1) << boost::any_cast(value); - else if (value.type() == typeid(char*)) - ss << '\'' << Base::Tools::escapedUnicodeFromUtf8(boost::any_cast(value)) << '\''; - else if (value.type() == typeid(const char*)) - ss << '\'' << Base::Tools::escapedUnicodeFromUtf8(boost::any_cast(value)) << '\''; - else if (value.type() == typeid(std::string)) - ss << '\'' << Base::Tools::escapedUnicodeFromUtf8(boost::any_cast(value).c_str()) << '\''; - else if (value.type() == typeid(int)) - ss << boost::any_cast(value); - else if (value.type() == typeid(unsigned int)) - ss << boost::any_cast(value); - else if (value.type() == typeid(short)) - ss << boost::any_cast(value); - else if (value.type() == typeid(unsigned short)) - ss << boost::any_cast(value); - else if (value.type() == typeid(char)) - ss << boost::any_cast(value); - else if (value.type() == typeid(unsigned char)) - ss << boost::any_cast(value); +const std::string &ObjectIdentifier::getSubObjectName() const { + return subObjectName.getString(); +} + +void ObjectIdentifier::importSubNames(const ObjectIdentifier::SubNameMap &subNameMap) +{ + if(!owner || !owner->getDocument()) + return; + ResolveResults result(*this); + auto it = subNameMap.find(std::make_pair(result.resolvedDocumentObject,std::string())); + if(it!=subNameMap.end()) { + auto obj = owner->getDocument()->getObject(it->second.c_str()); + if(!obj) { + FC_ERR("Failed to find import object " << it->second << " from " + << result.resolvedDocumentObject->getFullName()); + return; + } + documentNameSet = false; + documentName.str.clear(); + if(documentObjectName.isRealString()) + documentObjectName.str = obj->Label.getValue(); + else + documentObjectName.str = obj->getNameInDocument(); + _cache.clear(); + } + if(subObjectName.getString().empty()) + return; + it = subNameMap.find(std::make_pair( + result.resolvedDocumentObject,subObjectName.str)); + if(it==subNameMap.end()) + return; + subObjectName = String(it->second,true); + _cache.clear(); + shadowSub.first.clear(); + shadowSub.second.clear(); +} + +bool ObjectIdentifier::updateElementReference(ExpressionVisitor &v, + App::DocumentObject *feature, bool reverse) +{ + assert(v.getPropertyLink()); + if(subObjectName.getString().empty()) + return false; + + ResolveResults result(*this); + if(!result.resolvedSubObject) + return false; + if(v.getPropertyLink()->_updateElementReference( + feature,result.resolvedDocumentObject,subObjectName.str,shadowSub,reverse)) { + _cache.clear(); + v.aboutToChange(); + return true; + } + return false; +} + +bool ObjectIdentifier::adjustLinks(ExpressionVisitor &v, const std::set &inList) { + ResolveResults result(*this); + if(!result.resolvedDocumentObject) + return false; + if(result.resolvedSubObject) { + PropertyLinkSub prop; + prop.setValue(result.resolvedDocumentObject, {subObjectName.getString()}); + if(prop.adjustLink(inList)) { + v.aboutToChange(); + documentObjectName = String(prop.getValue()->getNameInDocument(),false,true); + subObjectName = String(prop.getSubValues().front(),true); + _cache.clear(); + return true; + } + } + return false; +} + +bool ObjectIdentifier::isTouched() const { + try { + ResolveResults result(*this); + if(result.resolvedProperty) { + if(result.propertyType==PseudoNone) + return result.resolvedProperty->isTouched(); + else + return result.resolvedDocumentObject->isTouched(); + } + }catch(...) {} + return false; +} + +void ObjectIdentifier::resolveAmbiguity() { + if(!owner || !owner->getNameInDocument() || isLocalProperty() || + (documentObjectNameSet && documentObjectName.getString().size() && + (documentObjectName.isRealString() || documentObjectName.isForceIdentifier()))) + { + return; + } + + ResolveResults result(*this); + resolveAmbiguity(result); +} + +void ObjectIdentifier::resolveAmbiguity(ResolveResults &result) { + + if(!result.resolvedDocumentObject) + return; + + if(result.propertyIndex==1) + components.erase(components.begin()); + + String subname = subObjectName; + if(result.resolvedDocumentObject == owner) { + setDocumentObjectName(owner,false,std::move(subname)); + }else if(result.flags.test(ResolveByIdentifier)) + setDocumentObjectName(std::move(result.resolvedDocumentObject),true,std::move(subname)); else - throw std::bad_cast(); + setDocumentObjectName( + String(result.resolvedDocumentObject->Label.getStrValue(),true,false),true,std::move(subname)); - Base::Interpreter().runString(ss.str().c_str()); + if(result.resolvedDocumentObject->getDocument() == owner->getDocument()) + setDocumentName(String()); } /** Construct and initialize a ResolveResults object, given an ObjectIdentifier instance. @@ -1149,27 +1868,53 @@ void ObjectIdentifier::setValue(const boost::any &value) const */ ObjectIdentifier::ResolveResults::ResolveResults(const ObjectIdentifier &oi) - : propertyIndex(-1) + : propertyIndex(0) , resolvedDocument(0) , resolvedDocumentName() , resolvedDocumentObject(0) , resolvedDocumentObjectName() + , resolvedSubObject(0) , resolvedProperty(0) , propertyName() + , propertyType(PseudoNone) { oi.resolve(*this); } std::string ObjectIdentifier::ResolveResults::resolveErrorString() const { - if (resolvedDocument == 0) - return std::string("Document not found: ") + resolvedDocumentName.toString(); - else if (resolvedDocumentObject == 0) - return std::string("Document object not found: ") + resolvedDocumentObjectName.toString(); - else if (resolvedProperty == 0) - return std::string("Property not found: ") + propertyName; + std::ostringstream ss; + if (resolvedDocument == 0) { + if(flags.test(ResolveAmbiguous)) + ss << "Ambiguous document name/label '" + << resolvedDocumentName.getString() << "'"; + else + ss << "Document '" << resolvedDocumentName.toString() << "' not found"; + } else if (resolvedDocumentObject == 0) { + if(flags.test(ResolveAmbiguous)) + ss << "Ambiguous document object name '" + << resolvedDocumentObjectName.getString() << "'"; + else + ss << "Document object '" << resolvedDocumentObjectName.toString() + << "' not found"; + } else if (subObjectName.getString().size() && resolvedSubObject == 0) { + ss << "Sub-object '" << resolvedDocumentObjectName.getString() + << '.' << subObjectName.toString() << "' not found"; + } else if (resolvedProperty == 0) { + if(propertyType != PseudoShape && + subObjectName.getString().size() && + !boost::ends_with(subObjectName.getString(),".")) + { + ss << "Non geometry subname reference must end with '.'"; + }else + ss << "Property '" << propertyName << "' not found"; + } - assert(false); + return ss.str(); +} - return ""; +void ObjectIdentifier::ResolveResults::getProperty(const ObjectIdentifier &oi) { + resolvedProperty = oi.resolveProperty( + resolvedDocumentObject,propertyName.c_str(),resolvedSubObject,propertyType); } + diff --git a/src/App/ObjectIdentifier.h b/src/App/ObjectIdentifier.h index 4eede9950098..fd1e170d3cea 100644 --- a/src/App/ObjectIdentifier.h +++ b/src/App/ObjectIdentifier.h @@ -24,39 +24,86 @@ #ifndef APP_PATH_H #define APP_PATH_H +#include +#include #include #include +#include +#include +#include #ifndef BOOST_105400 #include #else #include #endif +#include namespace App { +using any = boost::any; + +template +inline const T &any_cast(const boost::any &value) { + return boost::any_cast(value); +} + +template +inline T &any_cast(boost::any &value) { + return boost::any_cast(value); +} + class Property; class Document; class PropertyContainer; class DocumentObject; +class ExpressionVisitor; + +AppExport std::string quote(const std::string &input, bool toPython=false); -AppExport std::string quote(const std::string &input); +// Unfortunately VS2013 does not support default move constructor, so we have +// to implement them manually +#define FC_DEFAULT_CTORS(_t) \ + _t(const _t &) = default;\ + _t &operator=(const _t &) = default;\ + _t(_t &&other) { *this = std::move(other); }\ + _t &operator=(_t &&other) class AppExport ObjectIdentifier { public: + class AppExport DocumentMapper { + public: + DocumentMapper(const std::map &); + ~DocumentMapper(); + }; + class String { + friend class ObjectIdentifier; public: // Constructor - String(const std::string & s = "", bool _isRealString = false, bool _forceIdentifier = false) : str(s), isString(_isRealString), forceIdentifier(_forceIdentifier) { } + String(const std::string & s = "", bool _isRealString = false, bool _forceIdentifier = false) + : str(s), isString(_isRealString), forceIdentifier(_forceIdentifier) + { } + + String(std::string &&s, bool _isRealString = false, bool _forceIdentifier = false) + : str(std::move(s)), isString(_isRealString), forceIdentifier(_forceIdentifier) + { } + + FC_DEFAULT_CTORS(String) { + str = std::move(other.str); + isString = other.isString; + forceIdentifier = other.forceIdentifier; + return *this; + } // Accessors /** Returns the string */ - std::string getString() const { return str; } + const std::string &getString() const { return str; } /** Return true is string need to be quoted */ bool isRealString() const { return isString; } @@ -64,7 +111,7 @@ class AppExport ObjectIdentifier { bool isForceIdentifier() const { return forceIdentifier; } /** Returns a possibly quoted string */ - std::string toString() const; + std::string toString(bool toPython=false) const; // Operators @@ -82,6 +129,8 @@ class AppExport ObjectIdentifier { bool operator>(const String & other) const { return str > other.str; } + void checkImport(const App::DocumentObject *owner, + const App::DocumentObject *obj=0, String *objName=0); private: std::string str; @@ -103,22 +152,38 @@ class AppExport ObjectIdentifier { enum typeEnum { SIMPLE, MAP, - ARRAY + ARRAY, + RANGE, } ; public: // Constructors - - Component(const String &_component, typeEnum _type = SIMPLE, int _index = -1, String _key = String()); + FC_DEFAULT_CTORS(Component) { + name = std::move(other.name); + type = other.type; + begin = other.begin; + end = other.end; + step = other.step; + return *this; + } + + Component(const String &_name = String(), typeEnum _type=SIMPLE, + int begin=INT_MAX, int end=INT_MAX, int step=1); + Component(String &&_name, typeEnum _type=SIMPLE, + int begin=INT_MAX, int end=INT_MAX, int step=1); static Component SimpleComponent(const char * _component); static Component SimpleComponent(const String & _component); + static Component SimpleComponent(String &&_component); + + static Component ArrayComponent(int _index); - static Component ArrayComponent(const String &_component, int _index); + static Component RangeComponent(int _begin, int _end = INT_MAX, int _step=1); - static Component MapComponent(const String &_component, const String &_key); + static Component MapComponent(const String &_key); + static Component MapComponent(String &&_key); // Type queries @@ -128,93 +193,175 @@ class AppExport ObjectIdentifier { bool isArray() const { return type == ARRAY; } - // Accessors + bool isRange() const { return type == RANGE; } - std::string toString() const; + // Accessors - std::string getName() const { return name.getString(); } + void toString(std::ostream &ss, bool toPython=false) const; - std::size_t getIndex() const { return static_cast(index); } + const std::string &getName() const { return name.getString(); } - String getKey() const { return key; } + int getIndex() const {return begin;} + size_t getIndex(size_t count) const; - bool getKeyIsString() const { return keyIsString; } + int getBegin() const { return begin; } + int getEnd() const { return end; } + int getStep() const { return step; } // Operators bool operator==(const Component & other) const; + bool operator<(const Component & other) const; + + Py::Object get(const Py::Object &pyobj) const; + void set(Py::Object &pyobj, const Py::Object &value) const; + void del(Py::Object &pyobj) const; private: String name; typeEnum type; - int index; - String key; - bool keyIsString; - + int begin; + int end; + int step; friend class ObjectIdentifier; }; - ObjectIdentifier(const App::PropertyContainer * _owner = 0, const std::string & property = std::string()); + static Component SimpleComponent(const char * _component) + {return Component::SimpleComponent(_component);} + + static Component SimpleComponent(const String & _component) + {return Component::SimpleComponent(_component);} + + static Component SimpleComponent(String &&_component) + {return Component::SimpleComponent(std::move(_component));} + + static Component ArrayComponent(int _index) + {return Component::ArrayComponent(_index); } + + static Component RangeComponent(int _begin, int _end = INT_MAX, int _step=1) + {return Component::RangeComponent(_begin,_end,_step);} + + static Component MapComponent(const String &_key) + {return Component::MapComponent(_key);} - ObjectIdentifier(const App::Property & prop); + static Component MapComponent(String &&_key) + {return Component::MapComponent(_key);} + + ObjectIdentifier(const App::PropertyContainer * _owner = 0, + const std::string & property = std::string(), int index=INT_MAX); + + ObjectIdentifier(const App::PropertyContainer * _owner, bool localProperty); + + ObjectIdentifier(const App::Property & prop, int index=INT_MAX); + + FC_DEFAULT_CTORS(ObjectIdentifier) { + owner = other.owner; + documentName = std::move(other.documentName); + documentObjectName = std::move(other.documentObjectName); + subObjectName = std::move(other.subObjectName); + shadowSub = std::move(other.shadowSub); + components = std::move(other.components); + documentNameSet = other.documentNameSet; + documentObjectNameSet = other.documentObjectNameSet; + localProperty = other.localProperty; + _cache = std::move(other._cache); + _hash = std::move(other._hash); + return *this; + } virtual ~ObjectIdentifier() {} + App::DocumentObject *getOwner() const { return owner; } + + // Components + void addComponent(const Component &c) { + components.push_back(c); + _cache.clear(); + } + // Components - void addComponent(const Component &c) { components.push_back(c); } + void addComponent(Component &&c) { + components.push_back(std::move(c)); + _cache.clear(); + } + + std::string getPropertyName() const; template void addComponents(const C &cs) { components.insert(components.end(), cs.begin(), cs.end()); } - const std::string getPropertyName() const; - const Component & getPropertyComponent(int i) const; - std::string getSubPathStr() const; + Component & getPropertyComponent(int i); + + std::vector getPropertyComponents() const; + const std::vector &getComponents() const { return components; } + + std::string getSubPathStr(bool toPython=false) const; int numComponents() const; int numSubComponents() const; - virtual std::string toString() const; + const std::string &toString() const; + + std::string toPersistentString() const; std::string toEscapedString() const; - App::Property *getProperty() const; + bool isTouched() const; + + App::Property *getProperty(int *ptype=0) const; App::ObjectIdentifier canonicalPath() const; // Document-centric functions - void setDocumentName(const String & name, bool force = false); + void setDocumentName(String &&name, bool force = false); - const String getDocumentName() const; + String getDocumentName() const; - void setDocumentObjectName(const String & name, bool force = false); + void setDocumentObjectName(String &&name, bool force = false, + String &&subname = String(), bool checkImport=false); - const String getDocumentObjectName() const; + void setDocumentObjectName(const App::DocumentObject *obj, bool force = false, + String &&subname = String(), bool checkImport=false); - bool validDocumentObjectRename(const std::string &oldName, const std::string &newName); + bool hasDocumentObjectName(bool forced=false) const; - bool renameDocumentObject(const std::string & oldName, const std::string & newName); + bool isLocalProperty() const { return localProperty; } - bool validDocumentRename(const std::string &oldName, const std::string &newName); + String getDocumentObjectName() const; - bool renameDocument(const std::string &oldName, const std::string &newName); + const std::string &getSubObjectName(bool newStyle) const; + const std::string &getSubObjectName() const; - App::Document *getDocument(String name = String()) const; + typedef std::map,std::string> SubNameMap; + void importSubNames(const SubNameMap &subNameMap); - App::DocumentObject *getDocumentObject() const; + bool updateLabelReference(App::DocumentObject *, const std::string &, const char *); + + bool relabeledDocument(ExpressionVisitor &v, const std::string &oldLabel, const std::string &newLabel); + std::pair getDep(std::vector *labels=0) const; + + App::Document *getDocument(String name = String(), bool *ambiguous=0) const; + + App::DocumentObject *getDocumentObject() const; + std::vector getStringList() const; App::ObjectIdentifier relativeTo(const App::ObjectIdentifier & other) const; + bool replaceObject(ObjectIdentifier &res, const App::DocumentObject *parent, + App::DocumentObject *oldObj, App::DocumentObject *newObj) const; + // Operators App::ObjectIdentifier & operator<<(const Component & value); + App::ObjectIdentifier & operator<<(Component &&value); bool operator==(const ObjectIdentifier & other) const; @@ -224,12 +371,14 @@ class AppExport ObjectIdentifier { // Getter - boost::any getValue() const; + App::any getValue(bool pathValue=false, bool *isPseudoProperty=0) const; + + Py::Object getPyValue(bool pathValue=false, bool *isPseudoProperty=0) const; // Setter; is const because it does not alter the object state, // but does have a aide effect. - void setValue(const boost::any & value) const; + void setValue(const App::any & value) const; // Static functions @@ -237,6 +386,16 @@ class AppExport ObjectIdentifier { std::string resolveErrorString() const; + bool adjustLinks(ExpressionVisitor &v, const std::set &inList); + + bool updateElementReference(ExpressionVisitor &v, App::DocumentObject *feature=0, bool reverse=false); + + void resolveAmbiguity(); + + bool verify(const App::Property &prop, bool silent=false) const; + + std::size_t hash() const; + protected: struct ResolveResults { @@ -248,29 +407,72 @@ class AppExport ObjectIdentifier { String resolvedDocumentName; App::DocumentObject * resolvedDocumentObject; String resolvedDocumentObjectName; + String subObjectName; + App::DocumentObject * resolvedSubObject; App::Property * resolvedProperty; std::string propertyName; + int propertyType; + std::bitset<32> flags; std::string resolveErrorString() const; + void getProperty(const ObjectIdentifier &oi); }; - std::string getPythonAccessor() const; + friend struct ResolveResults; + + App::Property *resolveProperty(const App::DocumentObject *obj, + const char *propertyName, App::DocumentObject *&sobj,int &ptype) const; + + void getSubPathStr(std::ostream &ss, const ResolveResults &result, bool toPython=false) const; + + Py::Object access(const ResolveResults &rs, Py::Object *value=0) const; void resolve(ResolveResults & results) const; + void resolveAmbiguity(ResolveResults &results); - App::DocumentObject *getDocumentObject(const App::Document *doc, const String &name, bool &byIdentifier) const; + static App::DocumentObject *getDocumentObject( + const App::Document *doc, const String &name, std::bitset<32> &flags); - const App::PropertyContainer * owner; + App::DocumentObject * owner; String documentName; - bool documentNameSet; String documentObjectName; - bool documentObjectNameSet; + String subObjectName; + std::pair shadowSub; std::vector components; + bool documentNameSet; + bool documentObjectNameSet; + bool localProperty; +private: + std::string _cache; // Cached string represstation of this identifier + std::size_t _hash; // Cached hash of this string }; -std::size_t AppExport hash_value(const App::ObjectIdentifier & path); +inline std::size_t hash_value(const App::ObjectIdentifier & path) { + return path.hash(); +} +/** Helper function to convert Python object to/from App::any +* +* WARNING! Must hold Python global interpreter lock before calling these +* functions +*/ +//@{ +App::any AppExport pyObjectToAny(Py::Object pyobj, bool check=true); +Py::Object AppExport pyObjectFromAny(const App::any &value); +//@} +} + +namespace std { + +template<> +struct hash { + typedef App::ObjectIdentifier argument_type; + typedef std::size_t result_type; + inline result_type operator()(argument_type const& s) const { + return s.hash(); + } +}; } #endif diff --git a/src/App/Property.h b/src/App/Property.h index 2e5c881f4a22..666fb8f0b017 100644 --- a/src/App/Property.h +++ b/src/App/Property.h @@ -26,6 +26,7 @@ // Std. configurations +#include #include #ifndef BOOST_105400 #include @@ -35,6 +36,9 @@ #include #include +namespace Py { +class Object; +} namespace App { @@ -140,6 +144,11 @@ class AppExport Property : public Base::Persistence /// Get value of property virtual const boost::any getPathValue(const App::ObjectIdentifier & path) const; + /// Get Python value of property + virtual bool getPyPathValue(const App::ObjectIdentifier &, Py::Object &) const { + return false; + } + /// Convert p to a canonical representation of it virtual App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const; diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index 31f4b34b3e38..4aa336756f5e 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "Expression.h" #include "ExpressionVisitors.h" #include "PropertyExpressionEngine.h" @@ -42,44 +43,44 @@ using namespace App; using namespace Base; using namespace boost; -class ObjectDeletedExpressionVisitor : public ExpressionVisitor { -public: +TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyExpressionContainer , App::PropertyXLinkContainer); - ObjectDeletedExpressionVisitor(const App::DocumentObject * _obj) - : obj(_obj) - , found(false) - { - } +static std::set _ExprContainers; - /** - * @brief Visit each node in the expression, and if it is a VariableExpression object check if it references obj - * @param node Node to visit - */ +PropertyExpressionContainer::PropertyExpressionContainer() { + static bool inited; + if(!inited) { + inited = true; + GetApplication().signalRelabelDocument.connect(PropertyExpressionContainer::slotRelabelDocument); + } + _ExprContainers.insert(this); +} - void visit(Expression * node) { - VariableExpression *expr = freecad_dynamic_cast(node); +PropertyExpressionContainer::~PropertyExpressionContainer() { + _ExprContainers.erase(this); +} - if (expr && expr->getPath().getDocumentObject() == obj) - found = true; +void PropertyExpressionContainer::slotRelabelDocument(const App::Document &doc) { + // For use a private _ExprContainers to track all living + // PropertyExpressionContainer including those inside undo/redo stack, + // because document relabel is not undoable/redoable. + + if(doc.getOldLabel() != doc.Label.getValue()) { + for(auto prop : _ExprContainers) + prop->onRelabeledDocument(doc); } +} - bool isFound() const { return found; } - -private: - const App::DocumentObject * obj; - bool found; -}; +/////////////////////////////////////////////////////////////////////////////////////// -TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::Property); +TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::PropertyExpressionContainer); /** * @brief Construct a new PropertyExpressionEngine object. */ PropertyExpressionEngine::PropertyExpressionEngine() - : Property() - , AtomicPropertyChangeInterface() - , running(false) + : running(false) , validator(0) { } @@ -110,81 +111,74 @@ Property *PropertyExpressionEngine::Copy() const PropertyExpressionEngine * engine = new PropertyExpressionEngine(); for (ExpressionMap::const_iterator it = expressions.begin(); it != expressions.end(); ++it) - engine->expressions[it->first] = ExpressionInfo(boost::shared_ptr(it->second.expression->copy()), it->second.comment.c_str()); + engine->expressions[it->first] = ExpressionInfo(boost::shared_ptr(it->second.expression->copy())); engine->validator = validator; return engine; } -void PropertyExpressionEngine::Paste(const Property &from) +void PropertyExpressionEngine::hasSetValue() { - const PropertyExpressionEngine * fromee = static_cast(&from); - - AtomicPropertyChange signaller(*this); - -#ifndef USE_OLD_DAG - //maintain backlinks, verify that this property is owned by a DocumentObject - App::DocumentObject* parent = dynamic_cast(getContainer()); - if (parent) { - ExpressionMap::const_iterator i = expressions.begin(); - while (i != expressions.end()) { - std::set deps; - i->second.expression->getDeps(deps); - - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - - if (docObj && (docObj != parent)) - docObj->_removeBackLink(parent); + App::DocumentObject *owner = dynamic_cast(getContainer()); + if(!owner || !owner->getNameInDocument() || owner->isRestoring() || testFlag(LinkDetached)) { + PropertyExpressionContainer::hasSetValue(); + return; + } - ++j; - } - ++i; + std::set deps; + std::vector labels; + unregisterElementReference(); + UpdateElementReferenceExpressionVisitor v(*this); + for(auto &e : expressions) { + auto expr = e.second.expression; + if(expr) { + expr->getDepObjects(deps,&labels); + if(!restoring) + expr->visit(v); } } -#endif - expressions.clear(); + registerLabelReferences(std::move(labels)); - for (ExpressionMap::const_iterator it = fromee->expressions.begin(); it != fromee->expressions.end(); ++it) { - expressions[it->first] = ExpressionInfo(boost::shared_ptr(it->second.expression->copy()), it->second.comment.c_str()); + updateDeps(std::move(deps)); -#ifndef USE_OLD_DAG - if (parent) { - //maintain backlinks - std::set deps; - it->second.expression->getDeps(deps); - - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); + PropertyExpressionContainer::hasSetValue(); +} - if (docObj && (docObj != parent)) - docObj->_addBackLink(parent); +void PropertyExpressionEngine::Paste(const Property &from) +{ + const PropertyExpressionEngine * fromee = static_cast(&from); - ++j; - } - } -#endif + AtomicPropertyChange signaller(*this); - expressionChanged(it->first); + expressions.clear(); + for(auto &e : fromee->expressions) { + expressions[e.first] = ExpressionInfo( + boost::shared_ptr(e.second.expression->copy())); + expressionChanged(e.first); } - validator = fromee->validator; + signaller.tryInvoke(); } void PropertyExpressionEngine::Save(Base::Writer &writer) const { - writer.Stream() << writer.ind() << "" << std::endl; - writer.incInd(); + writer.Stream() << writer.ind() << "" << std::endl; + writer.incInd(); + } else { + writer.Stream() << "\" xlink=\"1\">" << std::endl; + writer.incInd(); + PropertyExpressionContainer::Save(writer); + } for (ExpressionMap::const_iterator it = expressions.begin(); it != expressions.end(); ++it) { - writer.Stream() << writer.ind() << "first.toString()) <<"\"" << - " expression=\"" << Property::encodeAttribute(it->second.expression->toString()) << "\""; - if (it->second.comment.size() > 0) - writer.Stream() << " comment=\"" << Property::encodeAttribute(it->second.comment) << "\""; + writer.Stream() << writer.ind() << "first.toString()) <<"\" expression=\"" + << Property::encodeAttribute(it->second.expression->toString(true)) << "\""; + if (it->second.expression->comment.size() > 0) + writer.Stream() << " comment=\"" + << Property::encodeAttribute(it->second.expression->comment) << "\""; writer.Stream() << "/>" << std::endl; } writer.decInd(); @@ -194,19 +188,22 @@ void PropertyExpressionEngine::Save(Base::Writer &writer) const void PropertyExpressionEngine::Restore(Base::XMLReader &reader) { reader.readElement("ExpressionEngine"); - int count = reader.getAttributeAsFloat("count"); - restoredExpressions.clear(); + if(reader.hasAttribute("xlink") && reader.getAttributeAsInteger("xlink")) + PropertyExpressionContainer::Restore(reader); + + restoredExpressions.reset(new std::vector); + restoredExpressions->reserve(count); for (int i = 0; i < count; ++i) { - DocumentObject * docObj = freecad_dynamic_cast(getContainer()); reader.readElement("Expression"); - ObjectIdentifier path = ObjectIdentifier::parse(docObj, reader.getAttribute("path")); - boost::shared_ptr expression(ExpressionParser::parse(docObj, reader.getAttribute("expression"))); - const char * comment = reader.hasAttribute("comment") ? reader.getAttribute("comment") : 0; - - restoredExpressions[path] = ExpressionInfo(expression, comment); + restoredExpressions->emplace_back(); + auto &info = restoredExpressions->back(); + info.path = reader.getAttribute("path"); + info.expr = reader.getAttribute("expression"); + if(reader.hasAttribute("comment")) + info.comment = reader.getAttribute("comment"); } reader.readEndElement("ExpressionEngine"); @@ -227,8 +224,6 @@ void PropertyExpressionEngine::buildGraphStructures(const ObjectIdentifier & pat boost::unordered_map & revNodes, std::vector & edges) const { - std::set deps; - /* Insert target property into nodes structure */ if (nodes.find(path) == nodes.end()) { int s = nodes.size(); @@ -239,26 +234,20 @@ void PropertyExpressionEngine::buildGraphStructures(const ObjectIdentifier & pat else revNodes[nodes[path]] = path; - /* Get the dependencies for this expression */ - expression->getDeps(deps); - /* Insert dependencies into nodes structure */ - std::set::const_iterator di = deps.begin(); - while (di != deps.end()) { - Property * prop = di->getProperty(); - - if (prop) { - ObjectIdentifier cPath(di->canonicalPath()); - - if (nodes.find(cPath) == nodes.end()) { - int s = nodes.size(); - - nodes[cPath] = s; + for(auto &dep : expression->getDeps()) { + for(auto &info : dep.second) { + if(info.first.empty()) + continue; + for(auto &oid : info.second) { + ObjectIdentifier cPath(oid.canonicalPath()); + if (nodes.find(cPath) == nodes.end()) { + int s = nodes.size(); + nodes[cPath] = s; + } + edges.push_back(std::make_pair(nodes[path], nodes[cPath])); } - - edges.push_back(std::make_pair(nodes[path], nodes[cPath])); } - ++di; } } @@ -268,7 +257,7 @@ void PropertyExpressionEngine::buildGraphStructures(const ObjectIdentifier & pat * @return New ObjectIdentifier */ -const ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier &p) const +ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdentifier &p) const { DocumentObject * docObj = freecad_dynamic_cast(getContainer()); @@ -276,15 +265,15 @@ const ObjectIdentifier PropertyExpressionEngine::canonicalPath(const ObjectIdent if (!docObj) throw Base::RuntimeError("PropertyExpressionEngine must be owned by a DocumentObject."); - Property * prop = p.getProperty(); + int ptype; + Property * prop = p.getProperty(&ptype); // p pointing to a property...? if (!prop) - throw Base::RuntimeError("Property not found"); + throw Base::RuntimeError(p.resolveErrorString().c_str()); - // ... in the same container as I? - if (prop->getContainer() != getContainer()) - throw Base::RuntimeError("Property does not belong to same container as PropertyExpressionEngine"); + if(ptype || prop->getContainer()!=getContainer()) + return p; // In case someone calls this with p pointing to a PropertyExpressionEngine for some reason if (prop->isDerivedFrom(PropertyExpressionEngine::classTypeId)) @@ -304,61 +293,37 @@ size_t PropertyExpressionEngine::numExpressions() const return expressions.size(); } -/** - * @brief Slot called when a document object is renamed. - * @param obj Renamed object - */ - -void PropertyExpressionEngine::slotObjectRenamed(const DocumentObject &obj) -{ -#ifdef FC_PROPERTYEXPRESSIONENGINE_LOG - std::clog << "Object " << obj.getOldLabel() << " renamed to " << obj.Label.getValue() << std::endl; -#endif - - DocumentObject * docObj = freecad_dynamic_cast(getContainer()); - - /* In a document object, and on undo stack? */ - if (!docObj || docObj->getNameInDocument() == 0) - return; - - RelabelDocumentObjectExpressionVisitor v(*this, obj.getOldLabel(), obj.Label.getStrValue()); - - for (ExpressionMap::iterator it = expressions.begin(); it != expressions.end(); ++it) { - bool changed = v.getChanged(); - - it->second.expression->visit(v); - - if (changed != v.getChanged()) - expressionChanged(it->first); - } -} - -void PropertyExpressionEngine::slotObjectDeleted(const DocumentObject &obj) +void PropertyExpressionEngine::afterRestore() { DocumentObject * docObj = freecad_dynamic_cast(getContainer()); + if(restoredExpressions && docObj) { + Base::FlagToggler flag(restoring); + AtomicPropertyChange signaller(*this); - /* In a document object, and on undo stack? */ - if (!docObj || docObj->getNameInDocument() == 0) - return; - - ObjectDeletedExpressionVisitor v(&obj); + PropertyExpressionContainer::afterRestore(); + ObjectIdentifier::DocumentMapper mapper(this->_DocMap); - for (ExpressionMap::iterator it = expressions.begin(); it != expressions.end(); ++it) { - it->second.expression->visit(v); - - if (v.isFound()) { - touch(); // Touch to force recompute; that will trigger a proper error - return; + for(auto &info : *restoredExpressions) { + ObjectIdentifier path = ObjectIdentifier::parse(docObj, info.path); + boost::shared_ptr expression(Expression::parse(docObj, info.expr.c_str())); + if(expression) + expression->comment = std::move(info.comment); + setValue(path, expression); } + signaller.tryInvoke(); } + restoredExpressions.reset(); } -void PropertyExpressionEngine::onDocumentRestored() -{ - AtomicPropertyChange signaller(*this); - - for (ExpressionMap::iterator it = restoredExpressions.begin(); it != restoredExpressions.end(); ++it) - setValue(it->first, it->second.expression, it->second.comment.size() > 0 ? it->second.comment.c_str() : 0); +void PropertyExpressionEngine::onContainerRestored() { + Base::FlagToggler flag(restoring); + unregisterElementReference(); + UpdateElementReferenceExpressionVisitor v(*this); + for(auto &e : expressions) { + auto expr = e.second.expression; + if(expr) + expr->visit(v); + } } /** @@ -387,7 +352,7 @@ const boost::any PropertyExpressionEngine::getPathValue(const App::ObjectIdentif * @param comment Optional comment. */ -void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::shared_ptr expr, const char *comment) +void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::shared_ptr expr) { ObjectIdentifier usePath(canonicalPath(path)); const Property * prop = usePath.getProperty(); @@ -402,83 +367,17 @@ void PropertyExpressionEngine::setValue(const ObjectIdentifier & path, boost::sh if (expr) { std::string error = validateExpression(usePath, expr); - if (error.size() > 0) throw Base::RuntimeError(error.c_str()); - AtomicPropertyChange signaller(*this); -#ifndef USE_OLD_DAG - // When overriding an ObjectIdentifier key then first remove - // the dependency caused by the expression as otherwise it happens - // that the same object dependency is added twice for the same - // identifier. This makes it impossible to properly clear dependencies - // and thus leads to topological errors on recompute. - // - // Verify that this property is owned by a DocumentObject - App::DocumentObject* parent = dynamic_cast(getContainer()); - if (parent) { - if (it != expressions.end() && it->second.expression) { - std::set deps; - it->second.expression->getDeps(deps); - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - if (docObj && (docObj != parent)) - docObj->_removeBackLink(parent); - ++j; - } - } - } -#endif - - expressions[usePath] = ExpressionInfo(expr, comment); - -#ifndef USE_OLD_DAG - //maintain the backlinks in the documentobject graph datastructure - if (parent) { - std::set deps; - expr->getDeps(deps); - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - if (docObj && (docObj != parent)) - docObj->_addBackLink(parent); - - ++j; - } - } -#endif - + expressions[usePath] = ExpressionInfo(expr); expressionChanged(usePath); - } - else { + signaller.tryInvoke(); + } else { AtomicPropertyChange signaller(*this); - -#ifndef USE_OLD_DAG - //verify that this property is owned by a DocumentObject - //verify that the ObjectIdentifier usePath is part of the expression map and - //that the expression is not null - App::DocumentObject* parent = dynamic_cast(getContainer()); - if (parent && it != expressions.end() && it->second.expression) { - //maintain the backlinks in the documentobject graph datastructure - std::set deps; - it->second.expression->getDeps(deps); - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - if (docObj && (docObj != parent)) - docObj->_removeBackLink(parent); - - ++j; - } - } -#endif - expressions.erase(usePath); expressionChanged(usePath); + signaller.tryInvoke(); } } @@ -509,14 +408,29 @@ struct cycle_detector : public boost::dfs_visitor<> { */ void PropertyExpressionEngine::buildGraph(const ExpressionMap & exprs, - boost::unordered_map & revNodes, DiGraph & g) const + boost::unordered_map & revNodes, + DiGraph & g, ExecuteOption option) const { boost::unordered_map nodes; std::vector edges; // Build data structure for graph - for (ExpressionMap::const_iterator it = exprs.begin(); it != exprs.end(); ++it) + for (ExpressionMap::const_iterator it = exprs.begin(); it != exprs.end(); ++it) { + if(option!=ExecuteAll) { + auto prop = it->first.getProperty(); + if(!prop) + throw Base::RuntimeError("Path does not resolve to a property."); + bool is_output = prop->testStatus(App::Property::Output)||(prop->getType()&App::Prop_Output); + if((is_output && option==ExecuteNonOutput) || (!is_output && option==ExecuteOutput)) + continue; + if(option == ExecuteOnRestore + && !prop->testStatus(Property::Transient) + && !(prop->getType() & Prop_Transient) + && !prop->testStatus(Property::EvalOnRestore)) + continue; + } buildGraphStructures(it->first, it->second.expression, nodes, revNodes, edges); + } // Create graph g = DiGraph(revNodes.size()); @@ -544,13 +458,13 @@ void PropertyExpressionEngine::buildGraph(const ExpressionMap & exprs, * order, in case properties depends on each other. */ -std::vector PropertyExpressionEngine::computeEvaluationOrder() +std::vector PropertyExpressionEngine::computeEvaluationOrder(ExecuteOption option) { std::vector evaluationOrder; boost::unordered_map revNodes; DiGraph g; - buildGraph(expressions, revNodes, g); + buildGraph(expressions, revNodes, g, option); /* Compute evaluation order for expressions */ std::vector c; @@ -569,7 +483,7 @@ std::vector PropertyExpressionEngine::computeEvaluationOr * @return StdReturn on success. */ -DocumentObjectExecReturn *App::PropertyExpressionEngine::execute() +DocumentObjectExecReturn *App::PropertyExpressionEngine::execute(ExecuteOption option, bool *touched) { DocumentObject * docObj = freecad_dynamic_cast(getContainer()); @@ -579,6 +493,24 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute() if (running) return DocumentObject::StdReturn; + if(option == ExecuteOnRestore) { + bool found = false; + for(auto &e : expressions) { + auto prop = e.first.getProperty(); + if(!prop) + continue; + if(prop->testStatus(App::Property::Transient) + || (prop->getType()&App::Prop_Transient) + || prop->testStatus(App::Property::EvalOnRestore)) + { + found = true; + break; + } + } + if(!found) + return DocumentObject::StdReturn; + } + /* Resetter class, to ensure that the "running" variable gets set to false, even if * an exception is thrown. */ @@ -595,7 +527,7 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute() resetter r(running); // Compute evaluation order - std::vector evaluationOrder = computeEvaluationOrder(); + std::vector evaluationOrder = computeEvaluationOrder(option); std::vector::const_iterator it = evaluationOrder.begin(); #ifdef FC_PROPERTYEXPRESSIONENGINE_LOG @@ -603,7 +535,7 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute() #endif /* Evaluate the expressions, and update properties */ - while (it != evaluationOrder.end()) { + for (;it != evaluationOrder.end();++it) { // Get property to update Property * prop = it->getProperty(); @@ -617,69 +549,28 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute() if (parent != docObj) throw Base::RuntimeError("Invalid property owner."); - // Evaluate expression - std::unique_ptr e(expressions[*it].expression->eval()); - -#ifdef FC_PROPERTYEXPRESSIONENGINE_LOG - { - Base::Quantity q; - boost::any value = e->getValueAsAny(); - - if (value.type() == typeid(Base::Quantity)) - q = boost::any_cast(value); - else if (value.type() == typeid(double)) - q = boost::any_cast(value); - else { - std::clog << "Unknown return value for expression."; - q = 0; + /* Set value of property */ + try { + // Evaluate expression + std::unique_ptr e(expressions[*it].expression->eval()); + auto value = e->getValueAsAny(); + if(option == ExecuteOnRestore && prop->testStatus(Property::EvalOnRestore)) { + if(isAnyEqual(value, prop->getPathValue(*it))) + continue; + if(touched) + *touched = true; } - - std::clog << "Assigning value " << q.getValue() << " to " << (*it).toString().c_str() << " (" << prop->getName() << ")" << std::endl; + prop->setPathValue(*it, value); + }catch(Base::Exception &e) { + std::ostringstream ss; + ss << e.what() << std::endl << "in property binding '" << prop->getName() << "'"; + e.setMessage(ss.str()); + throw; } -#endif - - /* Set value of property */ - prop->setPathValue(*it, e->getValueAsAny()); - - ++it; } return DocumentObject::StdReturn; } -/** - * @brief Find document objects that the expressions depend on. - * @param docObjs Dependencies - */ - -void PropertyExpressionEngine::getDocumentObjectDeps(std::vector &docObjs) const -{ - DocumentObject * owner = freecad_dynamic_cast(getContainer()); - - if (owner == 0) - return; - - ExpressionMap::const_iterator i = expressions.begin(); - - while (i != expressions.end()) { - std::set deps; - - i->second.expression->getDeps(deps); - - std::set::const_iterator j = deps.begin(); - - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - - if (docObj && docObj != owner) - docObjs.push_back(docObj); - - ++j; - } - ++i; - } -} - /** * @brief Find paths to document object. * @param obj Document object @@ -691,30 +582,16 @@ void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj, { DocumentObject * owner = freecad_dynamic_cast(getContainer()); - if (owner == 0) + if (owner == 0 || owner==obj) return; - ExpressionMap::const_iterator i = expressions.begin(); - - while (i != expressions.end()) { - std::set deps; - - i->second.expression->getDeps(deps); - - std::set::const_iterator j = deps.begin(); - - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - DocumentObject* docObj = p.getDocumentObject(); - - if (docObj == obj && docObj != owner) { - paths.push_back(i->first); - break; - } - - ++j; - } - ++i; + for(auto &v : expressions) { + const auto &deps = v.second.expression->getDeps(); + auto it = deps.find(obj); + if(it==deps.end()) + continue; + for(auto &dep : it->second) + paths.insert(paths.end(),dep.second.begin(),dep.second.end()); } } @@ -725,48 +602,12 @@ void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj, bool PropertyExpressionEngine::depsAreTouched() const { - ExpressionMap::const_iterator i = expressions.begin(); - - while (i != expressions.end()) { - std::set deps; - - i->second.expression->getDeps(deps); - - std::set::const_iterator j = deps.begin(); - - while (j != deps.end()) { - const ObjectIdentifier & p = *j; - Property* prop = p.getProperty(); - - if (prop && prop->isTouched()) - return true; - - ++j; - } - ++i; - } - + for(auto obj : _Deps) + if(obj->isTouched()) + return true; return false; } -/** - * @brief Get a map of all registered expressions. - * @return Map of expressions. - */ - -boost::unordered_map PropertyExpressionEngine::getExpressions() const -{ - boost::unordered_map result; - - ExpressionMap::const_iterator i = expressions.begin(); - while (i != expressions.end()) { - result.insert(std::make_pair(i->first, i->second)); - ++i; - } - - return result; -} - /** * @brief Validate the given path and expression. * @param path Object Identifier for expression. @@ -785,33 +626,16 @@ std::string PropertyExpressionEngine::validateExpression(const ObjectIdentifier return error; } - // Get dependencies from expression - std::set exprDeps; - expr->getDeps(exprDeps); - // Get document object DocumentObject * pathDocObj = usePath.getDocumentObject(); - - // Check for document object dependencies - for (std::set::const_iterator j = exprDeps.begin(); j != exprDeps.end(); ++j) { - DocumentObject * docObj = (*j).getDocumentObject(); - - // Skip internal dependencies; - if (docObj == pathDocObj) - continue; - - // Get dependencies for the document object pointed to be *j - std::vector targets; - targets.push_back(docObj); - - // Does the dependency resolve to a document? If not, ignore it - if ((*j).getDocument()) { - std::vector deps = (*j).getDocument()->getDependencyList(targets); - - for (std::vector::const_iterator i = deps.begin(); i != deps.end(); ++i) { - if (*i == pathDocObj) - return (*j).toString() + " reference creates a cyclic dependency."; - } + assert(pathDocObj); + + auto inList = pathDocObj->getInListEx(true); + for(auto docObj : expr->getDepObjects()) { + if(inList.count(docObj)) { + std::stringstream ss; + ss << "cyclic reference to " << docObj->getFullName(); + return ss.str(); } } @@ -899,3 +723,181 @@ void PropertyExpressionEngine::setPyObject(PyObject *) { throw Base::RuntimeError("Property is read-only"); } + +/* The policy implemented in the following function is to auto erase binding in + * case linked object is gone. I think it is better to cause error and get + * user's attension + * +void PropertyExpressionEngine::breakLink(App::DocumentObject *obj, bool clear) { + auto owner = dynamic_cast(getContainer()); + if(!owner) + return; + if(_Deps.count(obj)==0 && (!clear || obj!=owner || _Deps.empty())) + return; + AtomicPropertyChange signaler(*this); + for(auto it=expressions.begin(),itNext=it;it!=expressions.end();it=itNext) { + ++itNext; + const auto &deps = it->second.expression->getDepObjects(); + if(clear) { + // here means we are breaking all expression, except those that has + // no depdenecy or self dependency + if(deps.empty() || (deps.size()==1 && *deps.begin()==owner)) + continue; + }else if(!deps.count(obj)) + continue; + auto path = it->first; + expressions.erase(it); + expressionChanged(path); + } +} +*/ + +bool PropertyExpressionEngine::adjustLink(const std::set &inList) { + auto owner = dynamic_cast(getContainer()); + if(!owner) + return false; + bool found = false; + for(auto obj : _Deps) { + if(inList.count(obj)) { + found = true; + break; + } + } + if(!found) + return false; + + AtomicPropertyChange signaler(*this); + for(auto &v : expressions) { + try { + if(v.second.expression->adjustLinks(inList)) + expressionChanged(v.first); + }catch(Base::Exception &e) { + std::ostringstream ss; + ss << "Failed to adjust link for " << owner->getFullName() << " in expression " + << v.second.expression->toString() << ": " << e.what(); + throw Base::RuntimeError(ss.str()); + } + } + return true; +} + +void PropertyExpressionEngine::updateElementReference(DocumentObject *feature, bool reverse, bool notify) +{ + (void)notify; + if(!feature) + unregisterElementReference(); + UpdateElementReferenceExpressionVisitor v(*this,feature,reverse); + for(auto &e : expressions) { + e.second.expression->visit(v); + if(v.changed()) { + expressionChanged(e.first); + v.reset(); + } + } + if(feature && v.changed()) { + auto owner = dynamic_cast(getContainer()); + if(owner) + owner->onUpdateElementReference(this); + } +} + +bool PropertyExpressionEngine::referenceChanged() const { + return false; +} + +Property *PropertyExpressionEngine::CopyOnImportExternal( + const std::map &nameMap) const +{ + std::unique_ptr engine; + for(auto it=expressions.begin();it!=expressions.end();++it) { + boost::shared_ptr expr(it->second.expression->importSubNames(nameMap)); + if(!expr && !engine) + continue; + if(!engine) { + engine.reset(new PropertyExpressionEngine); + for(auto it2=expressions.begin();it2!=it;++it2) { + engine->expressions[it2->first] = ExpressionInfo( + boost::shared_ptr(it2->second.expression->copy())); + } + }else if(!expr) + expr = it->second.expression; + engine->expressions[it->first] = ExpressionInfo(expr); + } + if(!engine) + return 0; + engine->validator = validator; + return engine.release(); +} + +Property *PropertyExpressionEngine::CopyOnLabelChange(App::DocumentObject *obj, + const std::string &ref, const char *newLabel) const +{ + std::unique_ptr engine; + for(auto it=expressions.begin();it!=expressions.end();++it) { + boost::shared_ptr expr(it->second.expression->updateLabelReference(obj,ref,newLabel)); + if(!expr && !engine) + continue; + if(!engine) { + engine.reset(new PropertyExpressionEngine); + for(auto it2=expressions.begin();it2!=it;++it2) { + engine->expressions[it2->first] = ExpressionInfo( + boost::shared_ptr(it2->second.expression->copy())); + } + }else if(!expr) + expr = it->second.expression; + engine->expressions[it->first] = ExpressionInfo(expr); + } + if(!engine) + return 0; + engine->validator = validator; + return engine.release(); +} + +Property *PropertyExpressionEngine::CopyOnLinkReplace(const App::DocumentObject *parent, + App::DocumentObject *oldObj, App::DocumentObject *newObj) const +{ + std::unique_ptr engine; + for(auto it=expressions.begin();it!=expressions.end();++it) { + boost::shared_ptr expr( + it->second.expression->replaceObject(parent,oldObj,newObj)); + if(!expr && !engine) + continue; + if(!engine) { + engine.reset(new PropertyExpressionEngine); + for(auto it2=expressions.begin();it2!=it;++it2) { + engine->expressions[it2->first] = ExpressionInfo( + boost::shared_ptr(it2->second.expression->copy())); + } + }else if(!expr) + expr = it->second.expression; + engine->expressions[it->first] = ExpressionInfo(expr); + } + if(!engine) + return 0; + engine->validator = validator; + return engine.release(); +} + +std::map +PropertyExpressionEngine::getExpressions() const +{ + std::map res; + for(auto &v : expressions) + res[v.first] = v.second.expression.get(); + return res; +} + +void PropertyExpressionEngine::setExpressions( + std::map &&exprs) +{ + AtomicPropertyChange signaller(*this); + for(auto &v : exprs) + setValue(v.first,std::move(v.second)); +} + +void PropertyExpressionEngine::onRelabeledDocument(const App::Document &doc) +{ + RelabelDocumentExpressionVisitor v(doc); + for(auto &e : expressions) + e.second.expression->visit(v); +} diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 868ed2387b11..793de468434a 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -44,12 +44,39 @@ class DocumentObjectExecReturn; class ObjectIdentifier; class Expression; +class AppExport PropertyExpressionContainer : public App::PropertyXLinkContainer +{ + TYPESYSTEM_HEADER(); +public: + PropertyExpressionContainer(); + virtual ~PropertyExpressionContainer(); + + virtual std::map getExpressions() const = 0; + virtual void setExpressions(std::map &&exprs) = 0; + +protected: + virtual void onRelabeledDocument(const App::Document &doc) = 0; -class AppExport PropertyExpressionEngine : public App::Property, private App::AtomicPropertyChangeInterface +private: + static void slotRelabelDocument(const App::Document &doc); +}; + +class AppExport PropertyExpressionEngine : public App::PropertyExpressionContainer, + private App::AtomicPropertyChangeInterface { 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 &inList) override; + virtual Property *CopyOnImportExternal(const std::map &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 expr)> ValidatorFunc; /** @@ -58,22 +85,17 @@ class AppExport PropertyExpressionEngine : public App::Property, private App::At struct ExpressionInfo { boost::shared_ptr expression; /**< The actual expression tree */ - std::string comment; /**< Optional comment for this expression */ - ExpressionInfo(boost::shared_ptr expression = boost::shared_ptr(), const char * comment = 0) { + ExpressionInfo(boost::shared_ptr expression = boost::shared_ptr()) { 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 @@ class AppExport PropertyExpressionEngine : public App::Property, private App::At unsigned int getMemSize (void) const; + virtual std::map getExpressions() const override; + virtual void setExpressions(std::map &&exprs) override; + virtual void onRelabeledDocument(const App::Document &doc) override; + void setValue() { } // Dummy Property *Copy(void) const; @@ -93,20 +119,31 @@ class AppExport PropertyExpressionEngine : public App::Property, private App::At void Restore(Base::XMLReader &reader); - void setValue(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment = 0); + void setValue(const App::ObjectIdentifier &path, boost::shared_ptr expr); const boost::any getPathValue(const App::ObjectIdentifier & path) const; - DocumentObjectExecReturn * execute(); - - void getDocumentObjectDeps(std::vector & 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 & paths) const; bool depsAreTouched() const; - boost::unordered_map getExpressions() const; - /* Expression validator */ void setValidator(ValidatorFunc f) { validator = f; } @@ -116,45 +153,53 @@ class AppExport PropertyExpressionEngine : public App::Property, private App::At void renameObjectIdentifiers(const std::map & 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 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 Edge; typedef boost::unordered_map ExpressionMap; - std::vector computeEvaluationOrder(); + std::vector computeEvaluationOrder(ExecuteOption option); void buildGraphStructures(const App::ObjectIdentifier &path, const boost::shared_ptr expression, boost::unordered_map &nodes, boost::unordered_map &revNodes, std::vector &edges) const; void buildGraph(const ExpressionMap &exprs, - boost::unordered_map &revNodes, DiGraph &g) const; + boost::unordered_map &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 > restoredExpressions; friend class AtomicPropertyChange; diff --git a/src/App/PropertyGeo.cpp b/src/App/PropertyGeo.cpp index 298bbc91b399..ed9f7911cee8 100644 --- a/src/App/PropertyGeo.cpp +++ b/src/App/PropertyGeo.cpp @@ -39,7 +39,10 @@ #include #include #include +#include +#include "Document.h" +#include "DocumentObject.h" #include "Placement.h" #include "PropertyGeo.h" #include "ObjectIdentifier.h" @@ -193,14 +196,46 @@ void PropertyVector::Paste(const Property &from) void PropertyVector::getPaths(std::vector &paths) const { - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("x"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("y"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("z"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z"))); } +const boost::any PropertyVector::getPathValue(const ObjectIdentifier &path) const +{ + Base::Unit unit = getUnit(); + if(!unit.isEmpty()) { + std::string p = path.getSubPathStr(); + if (p == ".x" || p == ".y" || p == ".z") { + // Convert double to quantity + return Base::Quantity(boost::any_cast(Property::getPathValue(path)), unit); + } + } + return Property::getPathValue(path); +} + +bool PropertyVector::getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const +{ + Base::Unit unit = getUnit(); + if(unit.isEmpty()) + return false; + + std::string p = path.getSubPathStr(); + if (p == ".x") { + res = new QuantityPy(new Quantity(getValue().x,unit)); + } else if(p == ".y") { + res = new QuantityPy(new Quantity(getValue().y,unit)); + } else if(p == ".z") { + res = new QuantityPy(new Quantity(getValue().z,unit)); + } else + return false; + return true; +} + + //************************************************************************** // PropertyVectorDistance //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -216,18 +251,6 @@ PropertyVectorDistance::PropertyVectorDistance() } -const boost::any PropertyVectorDistance::getPathValue(const ObjectIdentifier &path) const -{ - std::string p = path.getSubPathStr(); - - if (p == ".x" || p == ".y" || p == ".z") { - // Convert double to quantity - return Base::Quantity(boost::any_cast(Property::getPathValue(path)), Unit::Length); - } - else - return Property::getPathValue(path); -} - PropertyVectorDistance::~PropertyVectorDistance() { @@ -253,18 +276,6 @@ PropertyPosition::~PropertyPosition() } -const boost::any PropertyPosition::getPathValue(const ObjectIdentifier &path) const -{ - std::string p = path.getSubPathStr(); - - if (p == ".x" || p == ".y" || p == ".z") { - // Convert double to quantity - return Base::Quantity(boost::any_cast(Property::getPathValue(path)), Unit::Length); - } - else - return Property::getPathValue(path); -} - //************************************************************************** // PropertyPosition //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -285,18 +296,6 @@ PropertyDirection::~PropertyDirection() } -const boost::any PropertyDirection::getPathValue(const ObjectIdentifier &path) const -{ - std::string p = path.getSubPathStr(); - - if (p == ".x" || p == ".y" || p == ".z") { - // Convert double to quantity - return Base::Quantity(boost::any_cast(Property::getPathValue(path)), Unit::Length); - } - else - return Property::getPathValue(path); -} - //************************************************************************** // PropertyVectorList //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -601,30 +600,30 @@ const Base::Placement & PropertyPlacement::getValue(void)const void PropertyPlacement::getPaths(std::vector &paths) const { - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Base")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("x"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Base")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("y"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Base")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("z"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Rotation")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Angle"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Rotation")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Axis")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("x"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Rotation")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Axis")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("y"))); - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Rotation")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("Axis")) - << ObjectIdentifier::Component::SimpleComponent(ObjectIdentifier::String("z"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Base")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Angle"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("x"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("y"))); + paths.push_back(ObjectIdentifier(*this) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Rotation")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("Axis")) + << ObjectIdentifier::SimpleComponent(ObjectIdentifier::String("z"))); } void PropertyPlacement::setPathValue(const ObjectIdentifier &path, const boost::any &value) @@ -669,6 +668,24 @@ const boost::any PropertyPlacement::getPathValue(const ObjectIdentifier &path) c return Property::getPathValue(path); } +bool PropertyPlacement::getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const +{ + std::string p = path.getSubPathStr(); + if (p == ".Rotation.Angle") { + Base::Vector3d axis; double angle; + _cPos.getRotation().getValue(axis,angle); + res = new QuantityPy(new Quantity(Base::toDegrees(angle),Unit::Angle)); + } else if (p == ".Base.x") { + res = new QuantityPy(new Quantity(_cPos.getPosition().x,Unit::Length)); + } else if (p == ".Base.y") { + res = new QuantityPy(new Quantity(_cPos.getPosition().y,Unit::Length)); + } else if (p == ".Base.z") { + res = new QuantityPy(new Quantity(_cPos.getPosition().z,Unit::Length)); + } else + return false; + return true; +} + PyObject *PropertyPlacement::getPyObject(void) { return new Base::PlacementPy(new Base::Placement(_cPos)); diff --git a/src/App/PropertyGeo.h b/src/App/PropertyGeo.h index b1110d19245c..c9c262099d4f 100644 --- a/src/App/PropertyGeo.h +++ b/src/App/PropertyGeo.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "Property.h" #include "PropertyLinks.h" @@ -98,6 +99,14 @@ class AppExport PropertyVector: public Property return sizeof(Base::Vector3d); } + virtual const boost::any getPathValue(const ObjectIdentifier &path) const override; + + virtual bool getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const override; + + virtual Base::Unit getUnit() const { + return Base::Unit(); + } + private: Base::Vector3d _cVec; }; @@ -120,7 +129,9 @@ class AppExport PropertyVectorDistance: public PropertyVector */ virtual ~PropertyVectorDistance(); - const boost::any getPathValue(const ObjectIdentifier &path) const; + virtual Base::Unit getUnit() const { + return Base::Unit::Length; + } const char* getEditorName(void) const { return "Gui::PropertyEditor::PropertyVectorDistanceItem"; @@ -144,7 +155,9 @@ class AppExport PropertyPosition: public PropertyVector */ virtual ~PropertyPosition(); - const boost::any getPathValue(const ObjectIdentifier &path) const; + virtual Base::Unit getUnit() const { + return Base::Unit::Length; + } const char* getEditorName(void) const { return "Gui::PropertyEditor::PropertyPositionItem"; @@ -168,7 +181,9 @@ class AppExport PropertyDirection: public PropertyVector */ virtual ~PropertyDirection(); - const boost::any getPathValue(const ObjectIdentifier &path) const; + virtual Base::Unit getUnit() const { + return Base::Unit::Length; + } const char* getEditorName(void) const { return "Gui::PropertyEditor::PropertyDirectionItem"; @@ -304,7 +319,9 @@ class AppExport PropertyPlacement: public Property void setPathValue(const ObjectIdentifier &path, const boost::any &value); - const boost::any getPathValue(const ObjectIdentifier &path) const; + virtual const boost::any getPathValue(const ObjectIdentifier &path) const override; + + virtual bool getPyPathValue(const ObjectIdentifier &path, Py::Object &res) const override; const char* getEditorName(void) const { return "Gui::PropertyEditor::PropertyPlacementItem"; diff --git a/src/App/PropertyUnits.cpp b/src/App/PropertyUnits.cpp index 64109174419b..eeb38a43e607 100644 --- a/src/App/PropertyUnits.cpp +++ b/src/App/PropertyUnits.cpp @@ -41,6 +41,7 @@ #include #include #include +#include "Expression.h" using namespace App; using namespace Base; @@ -120,7 +121,9 @@ void PropertyQuantity::setPyObject(PyObject *value) if (PyObject_TypeCheck(value, &(UnitPy::Type))) { Base::UnitPy *pcObject = static_cast(value); Base::Unit unit = *(pcObject->getUnitPtr()); + aboutToSetValue(); _Unit = unit; + hasSetValue(); } else { Base::Quantity quant= createQuantityFromPy(value); @@ -140,12 +143,12 @@ void PropertyQuantity::setPyObject(PyObject *value) void PropertyQuantity::setPathValue(const ObjectIdentifier & /*path*/, const boost::any &value) { - if (value.type() == typeid(double)) - setValue(boost::any_cast(value)); - else if (value.type() == typeid(Base::Quantity)) - setValue((boost::any_cast(value)).getValue()); - else - throw bad_cast(); + auto q = App::anyToQuantity(value); + aboutToSetValue(); + if(!q.getUnit().isEmpty()) + _Unit = q.getUnit(); + _dValue=q.getValue(); + setValue(q.getValue()); } const boost::any PropertyQuantity::getPathValue(const ObjectIdentifier & /*path*/) const diff --git a/src/App/Range.cpp b/src/App/Range.cpp index 62958b2fcdea..53f393140aac 100644 --- a/src/App/Range.cpp +++ b/src/App/Range.cpp @@ -107,11 +107,11 @@ bool Range::next() * @returns The row. */ -int App::decodeRow(const std::string &rowstr) +int App::decodeRow(const std::string &rowstr, bool silent) { int row = validRow(rowstr); - if (row >= 0) + if (silent || row >= 0) return row; else throw Base::IndexError("Invalid row specification."); @@ -126,11 +126,11 @@ int App::decodeRow(const std::string &rowstr) * */ -int App::decodeColumn(const std::string &colstr) +int App::decodeColumn(const std::string &colstr, bool silent) { int col = validColumn(colstr); - if (col >= 0) + if (silent || col >= 0) return col; else throw Base::IndexError("Invalid column specification"); @@ -201,19 +201,29 @@ int App::validColumn(const std::string &colstr) * */ -App::CellAddress App::stringToAddress(const char * strAddress) +App::CellAddress App::stringToAddress(const char * strAddress, bool silent) { - static const boost::regex e("\\${0,1}([A-Z]{1,2})\\${0,1}([0-9]{1,5})"); - boost::cmatch cm; - assert(strAddress != 0); - if (boost::regex_match(strAddress, cm, e)) { - const boost::sub_match colstr = cm[1]; - const boost::sub_match rowstr = cm[2]; + static boost::regex e("(\\$?[A-Z]{1,2})(\\$?[0-9]{1,5})"); + boost::cmatch cm; - return CellAddress(decodeRow(rowstr.str()), decodeColumn(colstr.str())); + if (boost::regex_match(strAddress, cm, e)) { + bool absCol = (cm[1].first[0]=='$'); + std::string r,c; + if(absCol) + c = std::string(cm[1].first+1,cm[1].second); + else + c = std::string(cm[1].first,cm[1].second); + bool absRow = (cm[2].first[0]=='$'); + if(absRow) + r = std::string(cm[2].first+1,cm[2].second); + else + r = std::string(cm[2].first,cm[2].second); + return CellAddress(decodeRow(r,silent), decodeColumn(c,silent), absRow, absCol); } + else if(silent) + return CellAddress(); else throw Base::RuntimeError("Invalid cell specifier."); } @@ -224,10 +234,12 @@ App::CellAddress App::stringToAddress(const char * strAddress) * @returns Address given as a string. */ -std::string App::CellAddress::toString() const +std::string App::CellAddress::toString(bool noAbsolute) const { std::stringstream s; + if(_absCol && !noAbsolute) + s << '$'; if (col() < 26) s << (char)('A' + col()); else { @@ -237,7 +249,21 @@ std::string App::CellAddress::toString() const s << (char)('A' + (colnum % 26)); } + if(_absRow && !noAbsolute) + s << '$'; s << (row() + 1); return s.str(); } + +bool App::CellAddress::parseAbsoluteAddress(const char *txt) { + if(txt[0]=='$' || (txt[0] && txt[1] && (txt[1]=='$' || txt[2]=='$'))) { + CellAddress addr = stringToAddress(txt,true); + if(addr.isValid()) { + *this = addr; + return true; + } + } + return false; +} + diff --git a/src/App/Range.h b/src/App/Range.h index 4f4fd41ec9c5..57aaa5c179e9 100644 --- a/src/App/Range.h +++ b/src/App/Range.h @@ -29,15 +29,17 @@ namespace App { struct CellAddress; -AppExport CellAddress stringToAddress(const char *strAddress); -AppExport int decodeColumn(const std::string &colstr); -AppExport int decodeRow(const std::string &rowstr); +AppExport CellAddress stringToAddress(const char *strAddress, bool silent=false); +AppExport int decodeColumn(const std::string &colstr, bool silent=false); +AppExport int decodeRow(const std::string &rowstr, bool silent=false); AppExport int validColumn(const std::string &colstr); AppExport int validRow(const std::string &rowstr); struct AppExport CellAddress { - CellAddress(int row = -1, int col = -1) : _row(row), _col(col) { } + CellAddress(int row = -1, int col = -1, bool absRow=false, bool absCol=false) + : _row(row), _col(col), _absRow(absRow), _absCol(absCol) + { } CellAddress(const char * address) { *this = stringToAddress(address); @@ -47,10 +49,16 @@ struct AppExport CellAddress { *this = stringToAddress(address.c_str()); } + bool parseAbsoluteAddress(const char *txt); + inline int row() const { return _row; } inline int col() const { return _col; } + void setRow(int r) { _row = r; } + + void setCol(int c) { _col = c; } + inline bool operator<(const CellAddress & other) const { return asInt() < other.asInt(); } inline bool operator==(const CellAddress & other) const { return asInt() == other.asInt(); } @@ -59,7 +67,11 @@ struct AppExport CellAddress { inline bool isValid() { return (row() >=0 && row() < MAX_ROWS && col() >= 0 && col() < MAX_COLUMNS); } - std::string toString() const; + inline bool isAbsoluteRow() const { return _absRow; } + + inline bool isAbsoluteCol() const { return _absCol; } + + std::string toString(bool noAbsolute=false) const; // Static members @@ -73,6 +85,8 @@ struct AppExport CellAddress { short _row; short _col; + bool _absRow; + bool _absCol; }; /** diff --git a/src/Mod/Sketcher/App/PropertyConstraintList.cpp b/src/Mod/Sketcher/App/PropertyConstraintList.cpp index 7837903989da..234ab91412ba 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.cpp +++ b/src/Mod/Sketcher/App/PropertyConstraintList.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -68,13 +69,13 @@ PropertyConstraintList::~PropertyConstraintList() App::ObjectIdentifier PropertyConstraintList::makeArrayPath(int idx) { - return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::ArrayComponent(ObjectIdentifier::String(getName()), idx); + return App::ObjectIdentifier(*this,idx); } App::ObjectIdentifier PropertyConstraintList::makeSimplePath(const Constraint * c) { - return App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName()) - << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String(c->Name, !ExpressionParser::isTokenAnIndentifier(c->Name))); + return App::ObjectIdentifier(*this) << App::ObjectIdentifier::SimpleComponent( + App::ObjectIdentifier::String(c->Name, !ExpressionParser::isTokenAnIndentifier(c->Name))); } App::ObjectIdentifier PropertyConstraintList::makePath(int idx, const Constraint * c) @@ -175,43 +176,52 @@ void PropertyConstraintList::setValue(const Constraint* lValue) void PropertyConstraintList::setValues(const std::vector& lValue) { + auto copy = lValue; + for(auto &cstr : copy) + cstr = cstr->clone(); aboutToSetValue(); - applyValues(lValue); + applyValues(std::move(copy)); hasSetValue(); } -void PropertyConstraintList::applyValues(const std::vector& lValue) +void PropertyConstraintList::setValues(std::vector&& lValue) { + aboutToSetValue(); + applyValues(std::move(lValue)); + hasSetValue(); +} + +void PropertyConstraintList::applyValues(std::vector&& lValue) { - std::vector oldVals(_lValueList); + std::set oldVals(_lValueList.begin(),_lValueList.end()); std::map renamed; std::set removed; + boost::unordered_map newValueMap; /* Check for renames */ for (unsigned int i = 0; i < lValue.size(); i++) { boost::unordered_map::const_iterator j = valueMap.find(lValue[i]->tag); - if (j != valueMap.end() && (i != j->second || _lValueList[j->second]->Name != lValue[i]->Name) ) { - App::ObjectIdentifier old_oid(makePath(j->second, _lValueList[j->second] )); - App::ObjectIdentifier new_oid(makePath(i, lValue[i])); - - renamed[old_oid] = new_oid; + if (j != valueMap.end()) { + if(i != j->second || _lValueList[j->second]->Name != lValue[i]->Name) { + App::ObjectIdentifier old_oid(makePath(j->second, _lValueList[j->second] )); + App::ObjectIdentifier new_oid(makePath(i, lValue[i])); + renamed[old_oid] = new_oid; + } + valueMap.erase(j); } - } - /* Update value map with new tags from new array */ - valueMap.clear(); - for (std::size_t i = 0; i < lValue.size(); i++) - valueMap[lValue[i]->tag] = i; + newValueMap[lValue[i]->tag] = i; + + // safety insurance in case new new values contain some pointers of the old values + oldVals.erase(lValue[i]); + } /* Collect info about removed elements */ - for (std::size_t i = 0; i < oldVals.size(); i++) { - boost::unordered_map::const_iterator j = valueMap.find(oldVals[i]->tag); - App::ObjectIdentifier oid(makePath(i, oldVals[i])); + for(auto &v : valueMap) + removed.insert(makePath(v.second,_lValueList[v.second])); - /* If not found in new values, place it in the set to be removed */ - if (j == valueMap.end()) - removed.insert(oid); - } + /* Update value map with new tags from new array */ + valueMap = std::move(newValueMap); /* Signal removes first, in case renamed values below have the same names as some of the removed ones. */ if (removed.size() > 0) @@ -221,16 +231,11 @@ void PropertyConstraintList::applyValues(const std::vector& lValue) if (renamed.size() > 0) signalConstraintsRenamed(renamed); - /* Resize array to new size */ - _lValueList.resize(lValue.size()); - - /* copy all objects */ - for (unsigned int i = 0; i < lValue.size(); i++) - _lValueList[i] = lValue[i]->clone(); + _lValueList = std::move(lValue); /* Clean-up; remove old values */ - for (unsigned int i = 0; i < oldVals.size(); i++) - delete oldVals[i]; + for(auto &v : oldVals) + delete v; } PyObject *PropertyConstraintList::getPyObject(void) @@ -241,6 +246,32 @@ PyObject *PropertyConstraintList::getPyObject(void) return list; } +bool PropertyConstraintList::getPyPathValue(const App::ObjectIdentifier &path, Py::Object &res) const { + if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName()) + return false; + + const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1); + + const Constraint *cstr = 0; + + if (c1.isArray()) + cstr = _lValueList[c1.getIndex(_lValueList.size())]; + else if (c1.isSimple()) { + ObjectIdentifier::Component c1 = path.getPropertyComponent(1); + for(auto c : _lValueList) { + if(c->Name == c1.getName()) { + cstr = c; + break; + } + } + } + if(!cstr) + return false; + Quantity q = cstr->getPresentationValue(); + res = new Base::QuantityPy(new Base::Quantity(q)); + return true; +} + void PropertyConstraintList::setPyObject(PyObject *value) { if (PyList_Check(value)) { @@ -307,26 +338,21 @@ void PropertyConstraintList::Restore(Base::XMLReader &reader) reader.readEndElement("ConstraintList"); // assignment - setValues(values); - for (Constraint* it : values) - delete it; + setValues(std::move(values)); } Property *PropertyConstraintList::Copy(void) const { PropertyConstraintList *p = new PropertyConstraintList(); p->applyValidGeometryKeys(validGeometryKeys); - p->applyValues(_lValueList); + p->setValues(_lValueList); return p; } void PropertyConstraintList::Paste(const Property &from) { const PropertyConstraintList& FromList = dynamic_cast(from); - aboutToSetValue(); - applyValues(FromList._lValueList); - applyValidGeometryKeys(FromList.validGeometryKeys); - hasSetValue(); + setValues(FromList._lValueList); } unsigned int PropertyConstraintList::getMemSize(void) const @@ -414,8 +440,7 @@ bool PropertyConstraintList::validConstraintName(const std::string & name) ObjectIdentifier PropertyConstraintList::createPath(int ConstrNbr) const { - return App::ObjectIdentifier(getContainer()) - << App::ObjectIdentifier::Component::ArrayComponent(App::ObjectIdentifier::String(getName()), ConstrNbr); + return App::ObjectIdentifier(*this,ConstrNbr); } int PropertyConstraintList::getIndexFromConstraintName(const string &name) @@ -425,21 +450,27 @@ int PropertyConstraintList::getIndexFromConstraintName(const string &name) void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const boost::any &value) { - const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0); + if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName()) + FC_THROWM(Base::ValueError,"invalid constraint path " << path.toString()); + + const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1); double dvalue; if (value.type() == typeid(double)) dvalue = boost::any_cast(value); + else if (value.type() == typeid(float)) + dvalue = App::any_cast(value); + else if (value.type() == typeid(long)) + dvalue = App::any_cast(value); + else if (value.type() == typeid(int)) + dvalue = App::any_cast(value); else if (value.type() == typeid(Quantity)) - dvalue = (boost::any_cast(value)).getValue(); + dvalue = (App::any_cast(value)).getValue(); else throw std::bad_cast(); - if (c0.isArray() && path.numSubComponents() == 1) { - int index = c0.getIndex(); - - if (c0.getIndex() >= _lValueList.size()) - throw Base::IndexError("Array out of bounds"); + if (c1.isArray()) { + size_t index = c1.getIndex(_lValueList.size()); switch (_lValueList[index]->Type) { case Angle: dvalue = Base::toRadians(dvalue); @@ -452,9 +483,7 @@ void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const bo hasSetValue(); return; } - else if (c0.isSimple() && path.numSubComponents() == 2) { - ObjectIdentifier::Component c1 = path.getPropertyComponent(1); - + else if (c1.isSimple()) { for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { int index = it - _lValueList.begin(); @@ -473,20 +502,20 @@ void PropertyConstraintList::setPathValue(const ObjectIdentifier &path, const bo } } } - throw Base::ValueError("Invalid constraint"); + FC_THROWM(Base::ValueError,"invalid constraint path " << path.toString()); } const Constraint * PropertyConstraintList::getConstraint(const ObjectIdentifier &path) const { - const ObjectIdentifier::Component & c0 = path.getPropertyComponent(0); + if(path.numSubComponents()!=2 || path.getPropertyComponent(0).getName()!=getName()) + FC_THROWM(Base::ValueError,"Invalid constraint path " << path.toString()); - if (c0.isArray() && path.numSubComponents() == 1) { - if (c0.getIndex() >= _lValueList.size()) - throw Base::IndexError("Array out of bounds"); + const ObjectIdentifier::Component & c1 = path.getPropertyComponent(1); - return _lValueList[c0.getIndex()]; + if (c1.isArray()) { + return _lValueList[c1.getIndex(_lValueList.size())]; } - else if (c0.isSimple() && path.numSubComponents() == 2) { + else if (c1.isSimple()) { ObjectIdentifier::Component c1 = path.getPropertyComponent(1); for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { @@ -494,7 +523,7 @@ const Constraint * PropertyConstraintList::getConstraint(const ObjectIdentifier return *it; } } - throw Base::ValueError("Invalid constraint"); + FC_THROWM(Base::ValueError,"Invalid constraint path " << path.toString()); } const boost::any PropertyConstraintList::getPathValue(const ObjectIdentifier &path) const @@ -502,31 +531,30 @@ const boost::any PropertyConstraintList::getPathValue(const ObjectIdentifier &pa return boost::any(getConstraint(path)->getPresentationValue()); } -const ObjectIdentifier PropertyConstraintList::canonicalPath(const ObjectIdentifier &p) const +ObjectIdentifier PropertyConstraintList::canonicalPath(const ObjectIdentifier &p) const { - const ObjectIdentifier::Component & c0 = p.getPropertyComponent(0); + if(p.numSubComponents()!=2 || p.getPropertyComponent(0).getName()!=getName()) + FC_THROWM(Base::ValueError,"Invalid constraint path " << p.toString()); + + const ObjectIdentifier::Component & c1 = p.getPropertyComponent(1); - if (c0.isArray() && p.numSubComponents() == 1) { - if (c0.getIndex() < _lValueList.size() && _lValueList[c0.getIndex()]->Name.size() > 0) - return ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent(_lValueList[c0.getIndex()]->Name); + if (c1.isArray()) { + size_t idx = c1.getIndex(); + if (idx < _lValueList.size() && _lValueList[idx]->Name.size() > 0) + return ObjectIdentifier(*this) << ObjectIdentifier::SimpleComponent(_lValueList[idx]->Name); return p; } - else if (c0.isSimple() && p.numSubComponents() == 2) { - ObjectIdentifier::Component c1 = p.getPropertyComponent(1); - - if (c1.isSimple()) - return p; + else if (c1.isSimple()) { + return p; } - throw Base::ValueError("Invalid constraint"); + FC_THROWM(Base::ValueError,"Invalid constraint path " << p.toString()); } void PropertyConstraintList::getPaths(std::vector &paths) const { for (std::vector::const_iterator it = _lValueList.begin(); it != _lValueList.end(); ++it) { if ((*it)->Name.size() > 0) - paths.push_back(ObjectIdentifier(getContainer()) << ObjectIdentifier::Component::SimpleComponent(getName()) - << ObjectIdentifier::Component::SimpleComponent((*it)->Name)); + paths.push_back(ObjectIdentifier(*this) << ObjectIdentifier::SimpleComponent((*it)->Name)); } } diff --git a/src/Mod/Sketcher/App/PropertyConstraintList.h b/src/Mod/Sketcher/App/PropertyConstraintList.h index b35e469abba2..40d83275a03b 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.h +++ b/src/Mod/Sketcher/App/PropertyConstraintList.h @@ -86,6 +86,13 @@ class SketcherExport PropertyConstraintList : public App::PropertyLists */ void setValues(const std::vector&); + /*! + Sets a vector of constraint to the property. + The values of the array are moved, and the ownership of constraints + inside are taken by this property + */ + void setValues(std::vector&&); + /*! Index operator \note If the geometry is invalid then the index operator @@ -124,9 +131,11 @@ class SketcherExport PropertyConstraintList : public App::PropertyLists const Constraint *getConstraint(const App::ObjectIdentifier &path) const; virtual void setPathValue(const App::ObjectIdentifier & path, const boost::any & value); virtual const boost::any getPathValue(const App::ObjectIdentifier & path) const; - virtual const App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const; + virtual App::ObjectIdentifier canonicalPath(const App::ObjectIdentifier & p) const; virtual void getPaths(std::vector & paths) const; + virtual bool getPyPathValue(const App::ObjectIdentifier &path, Py::Object &res) const override; + typedef std::pair ConstraintInfo ; boost::signals2::signal &)> signalConstraintsRenamed; @@ -153,7 +162,7 @@ class SketcherExport PropertyConstraintList : public App::PropertyLists std::vector validGeometryKeys; bool invalidGeometry; - void applyValues(const std::vector&); + void applyValues(std::vector&&); void applyValidGeometryKeys(const std::vector &keys); static std::vector _emptyValueList; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index c2b69a28c127..66f8de418203 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -6391,17 +6391,17 @@ std::string SketchObject::validateExpression(const App::ObjectIdentifier &path, return "Reference constraints cannot be set!"; } - std::set deps; - expr->getDeps(deps); - - for (std::set::const_iterator i = deps.begin(); i != deps.end(); ++i) { - const App::Property * prop = (*i).getProperty(); - - if (prop == &Constraints) { - const Constraint * constraint = Constraints.getConstraint(*i); - - if (!constraint->isDriving) - return "Reference constraint from this sketch cannot be used in this expression."; + auto deps = expr->getDeps(); + auto it = deps.find(this); + if(it!=deps.end()) { + auto it2 = it->second.find("Constraints"); + if(it2 != it->second.end()) { + for(auto &oid : it2->second) { + const Constraint * constraint = Constraints.getConstraint(oid); + + if (!constraint->isDriving) + return "Reference constraint from this sketch cannot be used in this expression."; + } } } return ""; @@ -6466,7 +6466,7 @@ void SketchObject::constraintsRemoved(const std::set &rem std::set::const_iterator i = removed.begin(); while (i != removed.end()) { - ExpressionEngine.setValue(*i, boost::shared_ptr(), 0); + ExpressionEngine.setValue(*i, boost::shared_ptr()); ++i; } } @@ -6858,9 +6858,9 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool return true; } -void SketchObject::setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment) +void SketchObject::setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr) { - DocumentObject::setExpression(path, expr, comment); + DocumentObject::setExpression(path, expr); if(noRecomputes) // if we do not have a recompute, the sketch must be solved to update the DoF of the solver, constraints and UI solve(); diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 8ce03b6a3219..05a608e866cb 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -412,7 +412,7 @@ class SketcherExport SketchObject : public Part::Part2DObject virtual void onDocumentRestored(); virtual void restoreFinished(); - virtual void setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr, const char * comment = 0); + virtual void setExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr); std::string validateExpression(const App::ObjectIdentifier &path, boost::shared_ptr expr);