From f0a9f97c40b8696366b1f155d076cf0a57678055 Mon Sep 17 00:00:00 2001 From: spo Date: Thu, 2 Jul 2015 15:27:44 +0300 Subject: [PATCH] Issue #588: Renaming a parameter used in an expression --- src/Model/Model_Data.cpp | 3 + src/ModelAPI/ModelAPI_Events.cpp | 55 +++ src/ModelAPI/ModelAPI_Events.h | 43 +++ .../ParametersPlugin_EvalListener.cpp | 337 ++++++++++++++---- .../ParametersPlugin_EvalListener.h | 18 +- .../ParametersPlugin_PyInterp.cpp | 51 +++ .../ParametersPlugin_PyInterp.h | 3 + 7 files changed, 443 insertions(+), 67 deletions(-) diff --git a/src/Model/Model_Data.cpp b/src/Model/Model_Data.cpp index 12f79ae5d..36f012028 100644 --- a/src/Model/Model_Data.cpp +++ b/src/Model/Model_Data.cpp @@ -77,6 +77,7 @@ std::string Model_Data::name() void Model_Data::setName(const std::string& theName) { bool isModified = false; + std::string anOldName = name(); Handle(TDataStd_Name) aName; if (!myLab.FindAttribute(TDataStd_Name::GetID(), aName)) { TDataStd_Name::Set(myLab, theName.c_str()); @@ -86,6 +87,8 @@ void Model_Data::setName(const std::string& theName) if (isModified) aName->Set(theName.c_str()); } + if (isModified) + ModelAPI_ObjectRenamedMessage::send(myObject, anOldName, theName, this); } AttributePtr Model_Data::addAttribute(const std::string& theID, const std::string theAttrType) diff --git a/src/ModelAPI/ModelAPI_Events.cpp b/src/ModelAPI/ModelAPI_Events.cpp index dbd088755..2260ce1fb 100644 --- a/src/ModelAPI/ModelAPI_Events.cpp +++ b/src/ModelAPI/ModelAPI_Events.cpp @@ -128,3 +128,58 @@ void ModelAPI_AttributeEvalMessage::setAttribute(AttributePtr theDocument) { myAttribute = theDocument; } + +ModelAPI_ObjectRenamedMessage::ModelAPI_ObjectRenamedMessage(const Events_ID theID, + const void* theSender) +: Events_Message(theID, theSender) +{ + +} + +ModelAPI_ObjectRenamedMessage::~ModelAPI_ObjectRenamedMessage() +{ + +} + +void ModelAPI_ObjectRenamedMessage::send(ObjectPtr theObject, + const std::string& theOldName, + const std::string& theNewName, + const void* theSender) +{ + std::shared_ptr aMessage( + new ModelAPI_ObjectRenamedMessage(eventId(), theSender)); + aMessage->setObject(theObject); + aMessage->setOldName(theOldName); + aMessage->setNewName(theNewName); + Events_Loop::loop()->send(aMessage); +} + +ObjectPtr ModelAPI_ObjectRenamedMessage::object() const +{ + return myObject; +} + +void ModelAPI_ObjectRenamedMessage::setObject(ObjectPtr theObject) +{ + myObject = theObject; +} + +std::string ModelAPI_ObjectRenamedMessage::oldName() const +{ + return myOldName; +} + +void ModelAPI_ObjectRenamedMessage::setOldName(const std::string& theOldName) +{ + myOldName = theOldName; +} + +std::string ModelAPI_ObjectRenamedMessage::newName() const +{ + return myNewName; +} + +void ModelAPI_ObjectRenamedMessage::setNewName(const std::string& theNewName) +{ + myNewName = theNewName; +} diff --git a/src/ModelAPI/ModelAPI_Events.h b/src/ModelAPI/ModelAPI_Events.h index 741aa0a8b..bcb50be1e 100644 --- a/src/ModelAPI/ModelAPI_Events.h +++ b/src/ModelAPI/ModelAPI_Events.h @@ -28,6 +28,8 @@ static const char * EVENT_OBJECT_CREATED = "ObjectCreated"; static const char * EVENT_OBJECT_UPDATED = "ObjectUpdated"; /// Event ID that data of feature is deleted (comes with Model_ObjectDeletedMessage) static const char * EVENT_OBJECT_DELETED = "ObjectDeleted"; +/// Event ID that name of feature is changed (comes with Model_ObjectRenamedMessage) +static const char * EVENT_OBJECT_RENAMED = "ObjectRenamed"; /// Event ID that data of feature is updated (comes with ModelAPI_ObjectUpdatedMessage) static const char * EVENT_OBJECT_MOVED = "ObjectsMoved"; /// Event ID that visualization must be redisplayed (comes with ModelAPI_ObjectUpdatedMessage) @@ -199,4 +201,45 @@ class ModelAPI_AttributeEvalMessage : public Events_Message MODELAPI_EXPORT void setAttribute(AttributePtr theAttribute); }; +/// Message that the object is renamed +class ModelAPI_ObjectRenamedMessage : public Events_Message +{ + ObjectPtr myObject; + std::string myOldName; + std::string myNewName; + + public: + /// Static. Returns EventID of the message. + MODELAPI_EXPORT static Events_ID& eventId() + { + static const char * MY_OBJECT_RENAMED_EVENT_ID("ObjectRenamed"); + static Events_ID anId = Events_Loop::eventByName(MY_OBJECT_RENAMED_EVENT_ID); + return anId; + } + + /// Useful method that creates and sends the AttributeEvalMessage event + MODELAPI_EXPORT static void send(ObjectPtr theObject, + const std::string& theOldName, + const std::string& theNewName, + const void* theSender); + + /// Creates an empty message + MODELAPI_EXPORT ModelAPI_ObjectRenamedMessage(const Events_ID theID, const void* theSender = 0); + /// The virtual destructor + MODELAPI_EXPORT virtual ~ModelAPI_ObjectRenamedMessage(); + + /// Returns an object + MODELAPI_EXPORT ObjectPtr object() const; + /// Sets an object + MODELAPI_EXPORT void setObject(ObjectPtr theObject); + /// Returns an old name + MODELAPI_EXPORT std::string oldName() const; + /// Sets an old name + MODELAPI_EXPORT void setOldName(const std::string& theOldName); + /// Returns a new name + MODELAPI_EXPORT std::string newName() const; + /// Sets a new name + MODELAPI_EXPORT void setNewName(const std::string& theNewName); +}; + #endif diff --git a/src/ParametersPlugin/ParametersPlugin_EvalListener.cpp b/src/ParametersPlugin/ParametersPlugin_EvalListener.cpp index 968c16769..96e280436 100644 --- a/src/ParametersPlugin/ParametersPlugin_EvalListener.cpp +++ b/src/ParametersPlugin/ParametersPlugin_EvalListener.cpp @@ -8,12 +8,17 @@ #include #include +#include #include #include +#include +#include #include +#include #include + #include #include #include @@ -26,6 +31,8 @@ ParametersPlugin_EvalListener::ParametersPlugin_EvalListener() Events_Loop* aLoop = Events_Loop::loop(); const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId(); aLoop->registerListener(this, kEvaluationEvent, NULL, true); + const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId(); + aLoop->registerListener(this, kObjectRenamedEvent, NULL, true); myInterp = std::shared_ptr(new ParametersPlugin_PyInterp()); myInterp->initialize(); @@ -35,77 +42,18 @@ ParametersPlugin_EvalListener::~ParametersPlugin_EvalListener() { } -void ParametersPlugin_EvalListener::processEvent(const std::shared_ptr& theMessage) +void ParametersPlugin_EvalListener::processEvent( + const std::shared_ptr& theMessage) { if (!theMessage.get()) return; const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId(); + const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId(); if (theMessage->eventID() == kEvaluationEvent) { - std::shared_ptr aMessage = - std::dynamic_pointer_cast(theMessage); - - // Double - AttributeDoublePtr aDoubleAttribute = - std::dynamic_pointer_cast(aMessage->attribute()); - if (aDoubleAttribute.get()) { - std::string anError; - double aValue = evaluate(aDoubleAttribute->text(), anError); - if (anError.empty()) { - aDoubleAttribute->setValue(aValue); - aDoubleAttribute->setExpressionInvalid(false); - } else { // set feature as invalid-parameter arguments - aDoubleAttribute->setExpressionInvalid(true); - } - } - - // Point - AttributePointPtr aPointAttribute = - std::dynamic_pointer_cast(aMessage->attribute()); - if (aPointAttribute.get()) { - std::string anError[3]; - double aValue[3] = { - evaluate(aPointAttribute->textX(), anError[0]), - evaluate(aPointAttribute->textY(), anError[1]), - evaluate(aPointAttribute->textZ(), anError[2]) - }; - bool isValid[3] = { - anError[0].empty(), - anError[1].empty(), - anError[2].empty() - }; - aPointAttribute->setExpressionInvalid(0, !isValid[0]); - aPointAttribute->setExpressionInvalid(1, !isValid[1]); - aPointAttribute->setExpressionInvalid(2, !isValid[2]); - - aPointAttribute->setValue( - isValid[0] ? aValue[0] : aPointAttribute->x(), - isValid[1] ? aValue[1] : aPointAttribute->y(), - isValid[2] ? aValue[2] : aPointAttribute->z() - ); - } - - // Point2D - AttributePoint2DPtr aPoint2DAttribute = - std::dynamic_pointer_cast(aMessage->attribute()); - if (aPoint2DAttribute.get()) { - std::string anError[2]; - double aValue[2] = { - evaluate(aPoint2DAttribute->textX(), anError[0]), - evaluate(aPoint2DAttribute->textY(), anError[1]) - }; - bool isValid[2] = { - anError[0].empty(), - anError[1].empty() - }; - aPoint2DAttribute->setExpressionInvalid(0, !isValid[0]); - aPoint2DAttribute->setExpressionInvalid(1, !isValid[1]); - - aPoint2DAttribute->setValue( - isValid[0] ? aValue[0] : aPoint2DAttribute->x(), - isValid[1] ? aValue[1] : aPoint2DAttribute->y() - ); - } + processEvaluationEvent(theMessage); + } else if (theMessage->eventID() == kObjectRenamedEvent) { + processObjectRenamedEvent(theMessage); } else { Events_Error::send(std::string("ParametersPlugin python interpreter, unhandled message caught: ") + theMessage->eventID().eventText()); @@ -134,3 +82,262 @@ double ParametersPlugin_EvalListener::evaluate(const std::string& theExpression, myInterp->clearLocalContext(); return result; } + +void ParametersPlugin_EvalListener::processEvaluationEvent( + const std::shared_ptr& theMessage) +{ + std::shared_ptr aMessage = + std::dynamic_pointer_cast(theMessage); + + // Double + AttributeDoublePtr aDoubleAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + if (aDoubleAttribute.get()) { + std::string anError; + double aValue = evaluate(aDoubleAttribute->text(), anError); + if (anError.empty()) { + aDoubleAttribute->setValue(aValue); + aDoubleAttribute->setExpressionInvalid(false); + } else { // set feature as invalid-parameter arguments + aDoubleAttribute->setExpressionInvalid(true); + } + } + + // Point + AttributePointPtr aPointAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + if (aPointAttribute.get()) { + std::string anError[3]; + double aValue[3] = { + evaluate(aPointAttribute->textX(), anError[0]), + evaluate(aPointAttribute->textY(), anError[1]), + evaluate(aPointAttribute->textZ(), anError[2]) + }; + bool isValid[3] = { + anError[0].empty(), + anError[1].empty(), + anError[2].empty() + }; + aPointAttribute->setExpressionInvalid(0, !isValid[0]); + aPointAttribute->setExpressionInvalid(1, !isValid[1]); + aPointAttribute->setExpressionInvalid(2, !isValid[2]); + + aPointAttribute->setValue( + isValid[0] ? aValue[0] : aPointAttribute->x(), + isValid[1] ? aValue[1] : aPointAttribute->y(), + isValid[2] ? aValue[2] : aPointAttribute->z() + ); + } + + // Point2D + AttributePoint2DPtr aPoint2DAttribute = + std::dynamic_pointer_cast(aMessage->attribute()); + if (aPoint2DAttribute.get()) { + std::string anError[2]; + double aValue[2] = { + evaluate(aPoint2DAttribute->textX(), anError[0]), + evaluate(aPoint2DAttribute->textY(), anError[1]) + }; + bool isValid[2] = { + anError[0].empty(), + anError[1].empty() + }; + aPoint2DAttribute->setExpressionInvalid(0, !isValid[0]); + aPoint2DAttribute->setExpressionInvalid(1, !isValid[1]); + + aPoint2DAttribute->setValue( + isValid[0] ? aValue[0] : aPoint2DAttribute->x(), + isValid[1] ? aValue[1] : aPoint2DAttribute->y() + ); + } +} + +std::string ParametersPlugin_EvalListener::renameInPythonExpression( + const std::string& theExpression, + const std::string& theOldName, + const std::string& theNewName) +{ + std::string anExpressionString = theExpression; + + std::list > aPositions = + myInterp->positions(anExpressionString, theOldName); + + if (aPositions.empty()) + return anExpressionString; + + std::map > aLines; + std::list >::const_iterator it = aPositions.begin(); + for (; it != aPositions.end(); ++it) + aLines[it->first].push_back(it->second); + + // Start renaming from the end to keep indexes if theNewName is longer then theOldName + std::map >::const_reverse_iterator ritLine = aLines.rbegin(); + for (; ritLine != aLines.rend(); ++ritLine) { + // Calculate the start of the line (find the aLineNo occurrence of "\n" ) + int aLineNo = ritLine->first - 1; + size_t aLineStart = 0; + for (int i = 0; i < aLineNo; ++i) + aLineStart = anExpressionString.find("\n", aLineStart) + 1; + + const std::list& aColOffsets = ritLine->second; + std::list::const_reverse_iterator ritOffset = aColOffsets.rbegin(); + for (; ritOffset != aColOffsets.rend(); ++ritOffset) { + int anOffset = *ritOffset; + anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName); + } + } + + return anExpressionString; +} + +void ParametersPlugin_EvalListener::renameInParameter( + std::shared_ptr theParameter, + const std::string& theOldName, + const std::string& theNewName) +{ + std::shared_ptr anExpressionAttribute = + theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID()); + + std::string anExpressionString = anExpressionAttribute->value(); + anExpressionString = renameInPythonExpression(anExpressionString, + theOldName, + theNewName); + anExpressionAttribute->setValue(anExpressionString); +} + +void ParametersPlugin_EvalListener::renameInAttribute( + std::shared_ptr theAttribute, + const std::string& theOldName, + const std::string& theNewName) +{ + // Double + AttributeDoublePtr aDoubleAttribute = + std::dynamic_pointer_cast(theAttribute); + if (aDoubleAttribute.get()) { + std::string anExpressionString = aDoubleAttribute->text(); + anExpressionString = renameInPythonExpression(anExpressionString, + theOldName, + theNewName); + aDoubleAttribute->setText(anExpressionString); + } + + // Point + AttributePointPtr aPointAttribute = + std::dynamic_pointer_cast(theAttribute); + if (aPointAttribute.get()) { + std::string anExpressionString[3] = { + aPointAttribute->textX(), + aPointAttribute->textY(), + aPointAttribute->textZ() + }; + for (int i = 0; i < 3; ++i) + anExpressionString[i] = renameInPythonExpression(anExpressionString[i], + theOldName, + theNewName); + aPointAttribute->setText(anExpressionString[0], + anExpressionString[1], + anExpressionString[2]); + } + + // Point2D + AttributePoint2DPtr aPoint2DAttribute = + std::dynamic_pointer_cast(theAttribute); + if (aPoint2DAttribute.get()) { + std::string anExpressionString[2] = { + aPoint2DAttribute->textX(), + aPoint2DAttribute->textY() + }; + for (int i = 0; i < 2; ++i) + anExpressionString[i] = renameInPythonExpression(anExpressionString[i], + theOldName, + theNewName); + aPoint2DAttribute->setText(anExpressionString[0], + anExpressionString[1]); + } +} + +void ParametersPlugin_EvalListener::processObjectRenamedEvent( + const std::shared_ptr& theMessage) +{ + std::shared_ptr aMessage = + std::dynamic_pointer_cast(theMessage); + + if (!aMessage.get() || aMessage->oldName().empty() || aMessage->newName().empty()) + return; + + // check that the renamed object is a result + std::shared_ptr aResultParameter = + std::dynamic_pointer_cast(aMessage->object()); + if (!aResultParameter.get()) + return; + + // get parameter feature for the result + std::shared_ptr aFeature = aResultParameter->document()->feature(aResultParameter); + std::shared_ptr aParameter = + std::dynamic_pointer_cast(aFeature); + if (!aParameter.get()) + return; + + // rename a parameter attributes + // short way: + //aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(aMessage->newName()); + //aParameter->execute(); + // manual way: + aParameter->data()->setName(aMessage->newName()); + aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(aMessage->newName()); + + // List of documents to process + std::list aDocList; + SessionPtr aSession = ModelAPI_Session::get(); + DocumentPtr aDocument = aSession->activeDocument(); + DocumentPtr aRootDocument = aSession->moduleDocument(); + aDocList.push_back(aDocument); + if (aDocument != aRootDocument) { + aDocList.push_back(aRootDocument); + } + + // Find parameters and rename in its expressions + for (std::list::const_iterator aDicumentIt = aDocList.begin(); + aDicumentIt != aDocList.end(); ++aDicumentIt) { + const DocumentPtr& aDocument = *aDicumentIt; + const int aSize = aDocument->size(ModelAPI_ResultParameter::group()); + for (int anIndex = 0; anIndex < aSize; ++anIndex) { + ResultParameterPtr aResultParameter = + std::dynamic_pointer_cast( + aDocument->object(ModelAPI_ResultParameter::group(), anIndex)); + if (!aResultParameter.get()) + continue; + + std::shared_ptr aParameter = + std::dynamic_pointer_cast( + aDocument->feature(aResultParameter)); + if (!aParameter.get()) + continue; + + // Rename + renameInParameter(aParameter, aMessage->oldName(), aMessage->newName()); + } + } + + // Find all features + for (std::list::const_iterator aDicumentIt = aDocList.begin(); + aDicumentIt != aDocList.end(); ++aDicumentIt) { + const DocumentPtr& aDocument = *aDicumentIt; + std::list aFeatures = aDocument->allFeatures(); + std::list::iterator aFeatureIt = aFeatures.begin(); + for (; aFeatureIt != aFeatures.end(); ++aFeatureIt) { + const FeaturePtr& aFeature = *aFeatureIt; + + // Find all attributes + std::list anAttributes = aFeature->data()->attributes(std::string()); + std::list::const_iterator anAttributeIt = anAttributes.begin(); + for (; anAttributeIt != anAttributes.end(); ++anAttributeIt) { + const AttributePtr& anAttribute = *anAttributeIt; + + // Rename + renameInAttribute(anAttribute, aMessage->oldName(), aMessage->newName()); + } + } + } +} + diff --git a/src/ParametersPlugin/ParametersPlugin_EvalListener.h b/src/ParametersPlugin/ParametersPlugin_EvalListener.h index 50c5727f2..bd9e6385f 100644 --- a/src/ParametersPlugin/ParametersPlugin_EvalListener.h +++ b/src/ParametersPlugin/ParametersPlugin_EvalListener.h @@ -11,6 +11,8 @@ #include #include +class ModelAPI_Attribute; +class ParametersPlugin_Parameter; class ParametersPlugin_PyInterp; class PARAMETERSPLUGIN_EXPORT ParametersPlugin_EvalListener : public Events_Listener @@ -22,8 +24,20 @@ class PARAMETERSPLUGIN_EXPORT ParametersPlugin_EvalListener : public Events_List virtual void processEvent(const std::shared_ptr& theMessage); protected: - double evaluate(const std::string& theExpression, - std::string& theError) ; + double evaluate(const std::string& theExpression, std::string& theError); + + void processEvaluationEvent(const std::shared_ptr& theMessage); + void processObjectRenamedEvent(const std::shared_ptr& theMessage); + + std::string renameInPythonExpression(const std::string& theExpression, + const std::string& theOldName, + const std::string& theNewName); + void renameInParameter(std::shared_ptr theParameter, + const std::string& theOldName, + const std::string& theNewName); + void renameInAttribute(std::shared_ptr theAttribute, + const std::string& theOldName, + const std::string& theNewName); private: std::shared_ptr myInterp; diff --git a/src/ParametersPlugin/ParametersPlugin_PyInterp.cpp b/src/ParametersPlugin/ParametersPlugin_PyInterp.cpp index 53659f23f..021f28552 100644 --- a/src/ParametersPlugin/ParametersPlugin_PyInterp.cpp +++ b/src/ParametersPlugin/ParametersPlugin_PyInterp.cpp @@ -19,6 +19,57 @@ ParametersPlugin_PyInterp::~ParametersPlugin_PyInterp() { } +const char* aSearchCode = + "import ast\n" + "class FindName(ast.NodeVisitor):\n" + " def __init__(self, name):\n" + " self.name = name\n" + " def visit_Name(self, node):\n" + " if node.id == self.name:\n" + " positions.append((node.lineno, node.col_offset))\n" + "FindName(name).visit(ast.parse(expression))"; + +std::list > +ParametersPlugin_PyInterp::positions(const std::string& theExpression, + const std::string& theName) +{ + PyLockWrapper lck; // Acquire GIL until the end of the method + + std::list > aResult; + + // prepare a context + PyObject* aContext = PyDict_New(); + PyObject* aBuiltinModule = PyImport_AddModule("__builtin__"); + PyDict_SetItemString(aContext, "__builtins__", aBuiltinModule); + + // extend aContext with variables + PyDict_SetItemString(aContext, "expression", PyString_FromString(theExpression.c_str())); + PyDict_SetItemString(aContext, "name", PyString_FromString(theName.c_str())); + PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]")); + + // run the search code + PyObject* aExecResult = PyRun_String(aSearchCode, Py_file_input, aContext, aContext); + Py_XDECREF(aExecResult); + + // receive results from context + PyObject* aPositions = PyDict_GetItemString(aContext, "positions"); + for (int anIndex = 0; anIndex < PyList_Size(aPositions); ++anIndex) { + PyObject* aPosition = PyList_GetItem(aPositions, anIndex); + PyObject* aLineNo = PyTuple_GetItem(aPosition, 0); + PyObject* aColOffset = PyTuple_GetItem(aPosition, 1); + + aResult.push_back( + std::pair((int)PyInt_AsLong(aLineNo), + (int)PyInt_AsLong(aColOffset))); + } + + // TODO(spo): after this refCount of the variable is not 0. Is there memory leak? + Py_DECREF(aContext); + + return aResult; +} + + std::list ParametersPlugin_PyInterp::compile(const std::string& theExpression) { PyLockWrapper lck; // Acquire GIL until the end of the method diff --git a/src/ParametersPlugin/ParametersPlugin_PyInterp.h b/src/ParametersPlugin/ParametersPlugin_PyInterp.h index 0a631f6e2..32b5c6d31 100644 --- a/src/ParametersPlugin/ParametersPlugin_PyInterp.h +++ b/src/ParametersPlugin/ParametersPlugin_PyInterp.h @@ -13,6 +13,7 @@ #include #include +#include class PARAMETERSPLUGIN_EXPORT ParametersPlugin_PyInterp : public PyInterp_Interp { @@ -20,6 +21,8 @@ class PARAMETERSPLUGIN_EXPORT ParametersPlugin_PyInterp : public PyInterp_Interp ParametersPlugin_PyInterp(); virtual ~ParametersPlugin_PyInterp(); + std::list > positions(const std::string& theExpression, + const std::string& theName); std::list compile(const std::string&); void extendLocalContext(const std::list&); void clearLocalContext(); -- 2.39.2