diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 00488e1561..439e3b53ef 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 5b5d07b01a..1fa1631e5c 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -372,7 +372,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()) { @@ -394,11 +394,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()) { @@ -486,8 +486,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()) { @@ -507,7 +507,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(); @@ -648,12 +648,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 6a341e253d..e81260e7d6 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -762,13 +762,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); } /** @@ -799,46 +797,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 f2ba8c09e4..fc620242b8 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -351,14 +351,12 @@ public: /* 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 { @@ -531,11 +529,6 @@ protected: // attributes /// 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 6489b5afdb..77ab7a99c2 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -176,6 +176,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 23e7fe7eba..b6911a60fa 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 @@ -785,6 +790,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 a246bb1aa9..b9cadab9fb 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,16 @@ #include #include +/** \defgroup Expression Expressions framework + \ingroup APP + \brief The expression system allows users to write expressions and formulas that produce values +*/ + +using namespace Base; +using namespace App; + +FC_LOG_LEVEL_INIT("Expression",true,true) + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -72,13 +83,86 @@ #pragma warning(disable : 4065) #endif -/** \defgroup Expression Expressions framework - \ingroup APP - \brief The expression system allows users to write expressions and formulas that produce values -*/ +#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) -using namespace Base; -using namespace App; +#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) { @@ -127,6 +211,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 // @@ -134,7 +666,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)) { } @@ -143,11 +675,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 // @@ -203,7 +968,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; } @@ -212,7 +977,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); } @@ -256,7 +1021,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); } @@ -275,7 +1040,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(); @@ -287,6 +1052,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 // @@ -317,31 +1089,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 @@ -355,7 +1102,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()); @@ -387,34 +1133,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() ); @@ -459,7 +1205,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; @@ -479,19 +1225,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: @@ -550,9 +1296,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(); } @@ -561,7 +1307,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()); } @@ -601,25 +1347,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 @@ -895,6 +1628,7 @@ Expression * FunctionExpression::evalAggregate() const Property * p = owner->getPropertyByName(range.address().c_str()); PropertyQuantity * qp; PropertyFloat * fp; + PropertyInteger * ip; if (!p) continue; @@ -903,6 +1637,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()); @@ -1182,12 +1918,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 << "; "; } @@ -1263,7 +1999,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; @@ -1280,22 +2016,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(); @@ -1303,7 +2024,6 @@ void FunctionExpression::visit(ExpressionVisitor &v) (*i)->visit(v); ++i; } - v.visit(this); } // @@ -1331,12 +2051,7 @@ VariableExpression::~VariableExpression() bool VariableExpression::isTouched() const { - try { - return getProperty()->isTouched(); - } - catch (...) { - return false; - } + return var.isTouched(); } /** @@ -1425,10 +2140,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). @@ -1445,7 +2171,7 @@ Expression *VariableExpression::simplify() const * Return a copy of the expression. */ -Expression *VariableExpression::copy() const +Expression *VariableExpression::_copy() const { return new VariableExpression(owner, var); } @@ -1455,18 +2181,119 @@ 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(std::set &props) const +void VariableExpression::_getDeps(ExpressionDeps &deps) const { - props.insert(var); + auto dep = var.getDep(); + if(dep.first) + deps[dep.first][dep.second].push_back(var); +} + +void VariableExpression::_getDepObjects( + std::set &deps, std::vector *labels) const +{ + auto dep = var.getDep(labels); + if(dep.first) + deps.insert(dep.first); +} + +void VariableExpression::_getIdentifiers(std::set &deps) const +{ + deps.insert(var); +} + +bool VariableExpression::_relabeledDocument(const std::string &oldName, + const std::string &newName, ExpressionVisitor &v) +{ + return var.relabeledDocument(v, oldName, newName); +} + +bool VariableExpression::_adjustLinks( + const std::set &inList, ExpressionVisitor &v) +{ + return var.adjustLinks(v,inList); +} + +void VariableExpression::_importSubNames(const ObjectIdentifier::SubNameMap &subNameMap) +{ + var.importSubNames(subNameMap); +} + +void VariableExpression::_updateLabelReference( + App::DocumentObject *obj, const std::string &ref, const char *newLabel) +{ + var.updateLabelReference(obj,ref,newLabel); +} + +bool VariableExpression::_updateElementReference( + App::DocumentObject *feature, bool reverse, ExpressionVisitor &v) +{ + 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) @@ -1474,24 +2301,53 @@ void VariableExpression::setPath(const ObjectIdentifier &path) var = path; } -bool VariableExpression::validDocumentObjectRename(const std::string &oldName, const std::string &newName) -{ - return var.validDocumentObjectRename(oldName, newName); +// +// PyObjectExpression class +// + +TYPESYSTEM_SOURCE(App::PyObjectExpression, App::Expression); + +PyObjectExpression::~PyObjectExpression() { + if(pyObj) { + Base::PyGILStateLocker lock; + Py::_XDECREF(pyObj); + } } -bool VariableExpression::renameDocumentObject(const std::string &oldName, const std::string &newName) -{ - return var.renameDocumentObject(oldName, newName); +Py::Object PyObjectExpression::getPyObject() const { + if(!pyObj) + return Py::Object(); + return Py::Object(pyObj); } -bool VariableExpression::validDocumentRename(const std::string &oldName, const std::string &newName) -{ - return var.validDocumentRename(oldName, newName); +void PyObjectExpression::setPyObject(Py::Object obj) { + Py::_XDECREF(pyObj); + pyObj = obj.ptr(); + Py::_XINCREF(pyObj); } -bool VariableExpression::renameDocument(const std::string &oldName, const std::string &newName) +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 { - return var.renameDocument(oldName, newName); + if(!pyObj) + return "None"; + else { + Base::PyGILStateLocker lock; + return Py::Object(pyObj).as_string(); + } +} + +Expression* PyObjectExpression::_copy() const +{ + return new PyObjectExpression(owner,pyObj,false); } // @@ -1524,7 +2380,7 @@ Expression *StringExpression::simplify() const return copy(); } -std::string StringExpression::toString() const +std::string StringExpression::toString(bool) const { return quote(text); } @@ -1538,7 +2394,7 @@ int StringExpression::priority() const * Return a copy of the expression. */ -Expression *StringExpression::copy() const +Expression *StringExpression::_copy() const { return new StringExpression(owner, text); } @@ -1594,11 +2450,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 + ")"; @@ -1609,7 +2465,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()); } @@ -1619,14 +2475,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); @@ -1641,12 +2490,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); } @@ -1663,7 +2512,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); } @@ -1671,14 +2520,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()); @@ -1695,14 +2543,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 @@ -1710,29 +2558,159 @@ int RangeExpression::priority() const return 20; } -void RangeExpression::getDeps(std::set &props) const -{ - Range i(range); - - do { - props.insert(ObjectIdentifier(owner, i.address())); - } while (i.next()); -} - Expression *RangeExpression::simplify() const { return copy(); } -void RangeExpression::setRange(const Range &r) +void RangeExpression::_getDeps(ExpressionDeps &deps) const { - range = r; + assert(owner); + + Range i(getRange()); + + auto &dep = deps[owner]; + do { + std::string address = i.address(); + dep[address].push_back(ObjectIdentifier(owner,address)); + } while (i.next()); +} + +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) +{ + (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::_moveCells(const CellAddress &address, + int rowCount, int colCount, ExpressionVisitor &v) +{ + 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. */ @@ -1948,10 +2926,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 e21f7415cc..f490be5b99 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 @@ public: 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: - const App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */ + 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: + App::DocumentObject * owner; /**< The document object used to access unqualified variables (i.e local scope) */ + +public: + std::string comment; }; /** @@ -125,9 +209,9 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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,35 +473,73 @@ public: 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); - - bool renameDocumentObject(const std::string & oldName, const std::string & newName); - - bool validDocumentRename(const std::string &oldName, const std::string &newName); - - bool renameDocument(const std::string &oldName, const std::string &newName); - const App::Property *getProperty() const; +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 &); + protected: ObjectIdentifier var; /**< Variable name */ }; +////////////////////////////////////////////////////////////////////// + +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; + +protected: + PyObject *pyObj = 0; +}; + /** * Class implementing a string. Used to signal either a genuine string or * a failed evaluation of an expression. @@ -436,13 +554,13 @@ public: 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 @@ public: 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; } - - void setRange(const Range & r); + Range getRange() const; protected: - Range range; + 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: + 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 4d5d9d1868..a1ae17ad86 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 @@ yyreduce: 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 @@ yyreduce: { /* 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 1789 "ExpressionParser.tab.c" /* yacc.c:1646 */ +#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 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 @@ yyreturn: #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 3982753c75..6ad9635d64 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 00f2e9289f..7eb290fa55 100644 --- a/src/App/ExpressionVisitors.h +++ b/src/App/ExpressionVisitors.h @@ -43,18 +43,8 @@ public: { } - 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 @@ private: 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() { - } - - /** - * @brief Visit each node in the expression, and if it is a VariableExpression object, incoke renameDocumentObject in it. - * @param node Node to visit - */ - - void visit(Expression * node) { - VariableExpression *expr = Base::freecad_dynamic_cast(node); - - if (expr && expr->validDocumentObjectRename(oldName, newName)) { - ExpressionModifier

::setExpressionChanged(); - expr->renameDocumentObject(oldName, newName); - } + void visit(Expression &node) { + this->updateElementReference(node,feature,reverse); } private: - std::string oldName; /**< Document object name to replace */ - std::string newName; /**< New document object name */ + App::DocumentObject *feature; + bool reverse; }; -template class RelabelDocumentExpressionVisitor : public ExpressionModifier

{ +// 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: - RelabelDocumentExpressionVisitor(P & prop, const std::string & _oldName, const std::string & _newName) - : ExpressionModifier

(prop) - , oldName(_oldName) - , newName(_newName) + RelabelDocumentExpressionVisitor(const App::Document &doc) + : doc(doc) { } - void visit(Expression * node) { - VariableExpression *expr = Base::freecad_dynamic_cast(node); - - if (expr && expr->validDocumentRename(oldName, newName)) { - ExpressionModifier

::setExpressionChanged(); - expr->renameDocument(oldName, newName); - } + void visit(Expression &node) { + this->relabeledDocument(node,doc.getOldLabel(),doc.Label.getStrValue()); } private: - std::string oldName; - std::string newName; + const App::Document &doc; +}; + +template class MoveCellsExpressionVisitor : public ExpressionModifier

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

(prop),address(address),rowCount(rowCount),colCount(colCount) + {} + + void visit(Expression &node) { + this->moveCells(node,address,rowCount,colCount); + } + +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) + {} + + void visit(Expression &node) { + this->offsetCells(node,rowOffset,colOffset); + } + +private: + int rowOffset; + int colOffset; }; } diff --git a/src/App/ObjectIdentifier.cpp b/src/App/ObjectIdentifier.cpp index 18c1ad1ba2..3807c4b1d4 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 (documentObjectNameSet) - s << documentObjectName.toString() << "."; - else if (result.propertyIndex > 0) - s << components[0].toString() << "."; + if(subObjectName.getString().size()) + s << subObjectName.toString() << '.'; - s << getPropertyName() << getSubPathStr(); + s << components[result.propertyIndex].getName(); + getSubPathStr(s,result); + const_cast(this)->_cache = s.str(); + return _cache; +} +std::string ObjectIdentifier::toPersistentString() const { + + if(!owner) + return std::string(); + + 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(result.resolvedDocument != obj->getDocument()) + return false; + + if(documentObjectName.getString().size()) { + if(documentObjectName.isForceIdentifier()) + return false; + + if(!documentObjectName.isRealString() && + documentObjectName.getString()==obj->getNameInDocument()) + return false; + + if(documentObjectName.getString()!=obj->Label.getValue()) + return false; + + documentObjectName = ObjectIdentifier::String(newLabel, true); + + _cache.clear(); + 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) { - if (ExpressionParser::isTokenAnIndentifier(newName)) - components[0].name = newName; - else - components[0].name = ObjectIdentifier::String(newName, true); - + 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 object - * @param newName New name of document object - */ - -bool ObjectIdentifier::validDocumentObjectRename(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 (documentObjectNameSet && documentObjectName == oldName) - return true; - else { - ResolveResults result(*this); - - if (result.propertyIndex == 1 && result.resolvedDocumentObjectName == oldName) - 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) - 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; + 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) { - documentName = newName; - 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) -{ - if (oldName == newName) - return false; - - if (documentNameSet && documentName == oldName) - 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; + 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 { - - /* 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; - } + 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()); + 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); +} - ResolveResults result(*this); +static const std::map *_DocumentMap; +ObjectIdentifier::DocumentMapper::DocumentMapper(const std::map &map) +{ + assert(!_DocumentMap); + _DocumentMap = ↦ +} - for (std::size_t i = result.propertyIndex; i < components.size(); ++i) - simplified << components[i]; - - Property * prop = getProperty(); - - // Invoke properties canonicalPath method, to let the property do the rest of the job. - - 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) { - documentObjectName = name; + 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) +{ + 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(;idx= 3 - return boost::any(PyUnicode_AsUTF8(pyvalue)); -#else - PyObject * s = PyUnicode_AsUTF8String(pyvalue); - destructor d2(s); - - return boost::any(PyString_AsString(s)); -#endif + 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) + return rs.resolvedProperty->getPathValue(*this); + + Base::PyGILStateLocker lock; + try { + return pyObjectToAny(access(rs)); + }catch(Py::Exception &) { + Base::PyException::ThrowException(); } - else { - throw Base::TypeError("Invalid property type."); + return App::any(); +} + +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; + } } + + if(rs.resolvedProperty && rs.propertyType==PseudoNone && pathValue) { + Py::Object res; + if(rs.resolvedProperty->getPyPathValue(*this,res)) + return res; + } + + 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"); - ss << getPythonAccessor() + " = "; + Base::PyGILStateLocker lock; + try { + Py::Object pyvalue = pyObjectFromAny(value); + access(rs,&pyvalue); + }catch(Py::Exception &) { + Base::PyException::ThrowException(); + } +} - 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(bool newStyle) const { + if(newStyle && shadowSub.first.size()) + return shadowSub.first; + if(shadowSub.second.size()) + return shadowSub.second; + return subObjectName.getString(); +} + +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 ""; + return ss.str(); } + +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 4eede99500..fd1e170d3c 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); +AppExport std::string quote(const std::string &input, bool toPython=false); + +// 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 @@ public: 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 @@ public: 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 @@ public: enum typeEnum { SIMPLE, MAP, - ARRAY + ARRAY, + RANGE, } ; public: // Constructors + 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 &_component, typeEnum _type = SIMPLE, int _index = -1, String _key = String()); + 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(const String &_component, int _index); + static Component ArrayComponent(int _index); - static Component MapComponent(const String &_component, const String &_key); + static Component RangeComponent(int _begin, int _end = INT_MAX, int _step=1); + + static Component MapComponent(const String &_key); + static Component MapComponent(String &&_key); // Type queries @@ -128,93 +193,175 @@ public: bool isArray() const { return type == ARRAY; } + bool isRange() const { return type == RANGE; } + // Accessors - std::string toString() const; + void toString(std::ostream &ss, bool toPython=false) const; - std::string getName() const { return name.getString(); } + const std::string &getName() const { return name.getString(); } - std::size_t getIndex() const { return static_cast(index); } + int getIndex() const {return begin;} + size_t getIndex(size_t count) const; - String getKey() const { return key; } - - 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);} - ObjectIdentifier(const App::Property & prop); + 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);} + + 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); } + void addComponent(const Component &c) { + components.push_back(c); + _cache.clear(); + } + + // Components + 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); + + 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 @@ public: // 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 @@ public: 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 @@ protected: 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 2e5c881f4a..666fb8f0b0 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 @@ public: /// 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 31f4b34b3e..4aa336756f 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; + +PropertyExpressionContainer::PropertyExpressionContainer() { + static bool inited; + if(!inited) { + inited = true; + GetApplication().signalRelabelDocument.connect(PropertyExpressionContainer::slotRelabelDocument); } + _ExprContainers.insert(this); +} - /** - * @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() { + _ExprContainers.erase(this); +} - void visit(Expression * node) { - VariableExpression *expr = freecad_dynamic_cast(node); - - 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::hasSetValue() +{ + App::DocumentObject *owner = dynamic_cast(getContainer()); + if(!owner || !owner->getNameInDocument() || owner->isRestoring() || testFlag(LinkDetached)) { + PropertyExpressionContainer::hasSetValue(); + return; + } + + 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); + } + } + registerLabelReferences(std::move(labels)); + + updateDeps(std::move(deps)); + + PropertyExpressionContainer::hasSetValue(); +} + void PropertyExpressionEngine::Paste(const Property &from) { 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); - - ++j; - } - ++i; - } - } -#endif expressions.clear(); - - 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()); - -#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(); - - if (docObj && (docObj != parent)) - docObj->_addBackLink(parent); - - ++j; - } - } -#endif - - expressionChanged(it->first); + 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; + PropertyExpressionContainer::afterRestore(); + ObjectIdentifier::DocumentMapper mapper(this->_DocMap); - ObjectDeletedExpressionVisitor v(&obj); - - 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; - } - - std::clog << "Assigning value " << q.getValue() << " to " << (*it).toString().c_str() << " (" << prop->getName() << ")" << std::endl; - } -#endif - /* Set value of property */ - prop->setPathValue(*it, e->getValueAsAny()); - - ++it; + 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; + } + 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; + } } 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(); + assert(pathDocObj); - // 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."; - } + 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 868ed2387b..793de46843 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -44,11 +44,38 @@ class DocumentObjectExecReturn; class ObjectIdentifier; class Expression; - -class AppExport PropertyExpressionEngine : public App::Property, private App::AtomicPropertyChangeInterface +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; + +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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 298bbc91b3..ed9f7911ce 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 b1110d1924..c9c262099d 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 @@ public: 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 @@ public: */ 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 @@ public: */ 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 @@ public: */ 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 @@ public: 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 6410917441..eeb38a43e6 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 62958b2fcd..53f393140a 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 4f4fd41ec9..57aaa5c179 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 @@ protected: 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 7837903989..234ab91412 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()); - 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); + const ObjectIdentifier::Component & c1 = p.getPropertyComponent(1); + + 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 b35e469abb..40d83275a0 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.h +++ b/src/Mod/Sketcher/App/PropertyConstraintList.h @@ -86,6 +86,13 @@ public: */ 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 @@ public: 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 @@ private: 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 c0c2d7b73c..9a3ad5ea9f 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -6393,17 +6393,17 @@ std::string SketchObject::validateExpression(const App::ObjectIdentifier &path, return "Reference constraints cannot be set!"; } - std::set deps; - expr->getDeps(deps); + 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); - 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."; + if (!constraint->isDriving) + return "Reference constraint from this sketch cannot be used in this expression."; + } } } return ""; @@ -6468,7 +6468,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; } } @@ -6860,9 +6860,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 0bc595ffde..d726218e1c 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -414,7 +414,7 @@ protected: 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);