Salome HOME
Fix for the issue #1444 : correct checking of upper and lower parameters, current...
[modules/shaper.git] / src / ParametersPlugin / ParametersPlugin_EvalListener.cpp
1 /*
2  * ParametersPlugin_EvalListener.cpp
3  *
4  *  Created on: Apr 28, 2015
5  *      Author: sbh
6  */
7
8 #include <pyconfig.h>
9
10 #include <ParametersPlugin_EvalListener.h>
11 #include <ParametersPlugin_Parameter.h>
12 #include <ParametersPlugin_PyInterp.h>
13
14 #include <Events_Error.h>
15
16 #include <ModelAPI_AttributeDouble.h>
17 #include <ModelAPI_AttributeInteger.h>
18 #include <ModelAPI_AttributeRefList.h>
19 #include <ModelAPI_AttributeString.h>
20 #include <ModelAPI_AttributeValidator.h>
21 #include <ModelAPI_Document.h>
22 #include <ModelAPI_Events.h>
23 #include <ModelAPI_Session.h>
24 #include <ModelAPI_Tools.h>
25
26 #include <GeomDataAPI_Point.h>
27 #include <GeomDataAPI_Point2D.h>
28
29 #include <string>
30 #include <set>
31 #include <sstream>
32
33 //------------------------------------------------------------------------------
34 // Tools
35
36 std::string toStdString(double theValue)
37 {
38   std::ostringstream sstream;
39   sstream << theValue;
40   size_t aPos = sstream.str().find(".");
41   std::string aPnt = "";
42   if (aPos == std::string::npos)
43     aPnt = ".";
44   return sstream.str() + aPnt;
45 }
46
47 std::set<std::string> toSet(const std::list<std::string>& theContainer)
48 {
49   return std::set<std::string>(theContainer.begin(), theContainer.end());
50 }
51
52 //------------------------------------------------------------------------------
53
54 ParametersPlugin_EvalListener::ParametersPlugin_EvalListener()
55 {
56   Events_Loop* aLoop = Events_Loop::loop();
57
58   Events_ID anEvents_IDs[] = {
59       ModelAPI_AttributeEvalMessage::eventId(),
60       ModelAPI_ObjectRenamedMessage::eventId(),
61       ModelAPI_ReplaceParameterMessage::eventId()
62   };
63
64   for (int i = 0; i < sizeof(anEvents_IDs)/sizeof(anEvents_IDs[0]); ++i)
65     aLoop->registerListener(this, anEvents_IDs[i], NULL, true);
66
67   myInterp = std::shared_ptr<ParametersPlugin_PyInterp>(new ParametersPlugin_PyInterp());
68   myInterp->initialize();
69 }
70
71 ParametersPlugin_EvalListener::~ParametersPlugin_EvalListener()
72 {
73 }
74
75 void ParametersPlugin_EvalListener::processEvent(
76     const std::shared_ptr<Events_Message>& theMessage)
77 {
78   if (!theMessage.get())
79     return;
80
81   const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId();
82   const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId();
83   const Events_ID kReplaceParameterEvent = ModelAPI_ReplaceParameterMessage::eventId();
84
85   if (theMessage->eventID() == kEvaluationEvent) {
86     processEvaluationEvent(theMessage);
87   } else if (theMessage->eventID() == kObjectRenamedEvent) {
88     processObjectRenamedEvent(theMessage);
89   } else if (theMessage->eventID() == kReplaceParameterEvent) {
90     processReplaceParameterEvent(theMessage);
91   } else {
92     Events_Error::send(std::string("ParametersPlugin python interpreter, unhandled message caught: ")
93                        + theMessage->eventID().eventText());
94   }
95 }
96
97 double ParametersPlugin_EvalListener::evaluate(FeaturePtr theParameter,
98   const std::string& theExpression, std::string& theError)
99 {
100   std::list<std::string> anExprParams = myInterp->compile(theExpression);
101   // find expression's params in the model
102   std::list<std::string> aContext;
103   std::list<std::string>::iterator it = anExprParams.begin();
104   for ( ; it != anExprParams.end(); it++) {
105     double aValue;
106     ResultParameterPtr aParamRes;
107     // If variable does not exist python interpreter will generate an error. It is OK.
108     if (!ModelAPI_Tools::findVariable(theParameter, *it, aValue, aParamRes, theParameter->document()))
109       continue;
110
111     aContext.push_back(*it + "=" + toStdString(aValue));
112   }
113   myInterp->extendLocalContext(aContext);
114   double result = myInterp->evaluate(theExpression, theError);
115   myInterp->clearLocalContext();
116   return result;
117 }
118
119 void ParametersPlugin_EvalListener::processEvaluationEvent(
120     const std::shared_ptr<Events_Message>& theMessage)
121 {
122   std::shared_ptr<ModelAPI_AttributeEvalMessage> aMessage =
123       std::dynamic_pointer_cast<ModelAPI_AttributeEvalMessage>(theMessage);
124
125   FeaturePtr aParamFeature = 
126     std::dynamic_pointer_cast<ModelAPI_Feature>(aMessage->attribute()->owner());
127   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeInteger::typeId()) {
128     AttributeIntegerPtr anAttribute =
129         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(aMessage->attribute());
130     std::string anError;
131     int aValue = (int)evaluate(aParamFeature, anAttribute->text(), anError);
132     bool isValid = anError.empty();
133     if (isValid)
134       anAttribute->setCalculatedValue(aValue);
135     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
136     anAttribute->setExpressionInvalid(!isValid);
137     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
138   } else
139   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) {
140     AttributeDoublePtr anAttribute =
141         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aMessage->attribute());
142     std::string anError;
143     double aValue = evaluate(aParamFeature, anAttribute->text(), anError);
144     bool isValid = anError.empty();
145     if (isValid)
146       anAttribute->setCalculatedValue(aValue);
147     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
148     anAttribute->setExpressionInvalid(!isValid);
149     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
150   } else
151   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) {
152     AttributePointPtr anAttribute =
153         std::dynamic_pointer_cast<GeomDataAPI_Point>(aMessage->attribute());
154     std::string aText[] = {
155       anAttribute->textX(),
156       anAttribute->textY(),
157       anAttribute->textZ()
158     };
159     double aCalculatedValue[] = {
160       anAttribute->x(),
161       anAttribute->y(),
162       anAttribute->z()
163     };
164     for (int i = 0; i < 3; ++i) {
165       std::string anError;
166       double aValue = evaluate(aParamFeature, aText[i], anError);
167       bool isValid = anError.empty();
168       if (isValid) aCalculatedValue[i] = aValue;
169       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
170       anAttribute->setExpressionInvalid(i, !isValid);
171       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
172     }
173     anAttribute->setCalculatedValue(aCalculatedValue[0],
174                                     aCalculatedValue[1],
175                                     aCalculatedValue[2]);
176   } else
177   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) {
178     AttributePoint2DPtr anAttribute =
179         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMessage->attribute());
180     std::string aText[] = {
181       anAttribute->textX(),
182       anAttribute->textY()
183     };
184     double aCalculatedValue[] = {
185       anAttribute->x(),
186       anAttribute->y()
187     };
188     for (int i = 0; i < 2; ++i) {
189       std::string anError;
190       double aValue = evaluate(aParamFeature, aText[i], anError);
191       bool isValid = anError.empty();
192       if (isValid) aCalculatedValue[i] = aValue;
193       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
194       anAttribute->setExpressionInvalid(i, !isValid);
195       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
196     }
197     anAttribute->setCalculatedValue(aCalculatedValue[0],
198                                     aCalculatedValue[1]);
199   }
200 }
201
202 std::string ParametersPlugin_EvalListener::renameInPythonExpression(
203     const std::string& theExpression,
204     const std::string& theOldName,
205     const std::string& theNewName)
206 {
207   std::string anExpressionString = theExpression;
208
209   std::list<std::pair<int, int> > aPositions =
210       myInterp->positions(anExpressionString, theOldName);
211
212   if (aPositions.empty())
213     return anExpressionString;
214
215   std::map<int, std::list<int> > aLines;
216   std::list<std::pair<int, int> >::const_iterator it = aPositions.begin();
217   for (; it != aPositions.end(); ++it)
218     aLines[it->first].push_back(it->second);
219
220   // Start renaming from the end to keep indexes if theNewName is longer then theOldName
221   std::map<int, std::list<int> >::const_reverse_iterator ritLine = aLines.rbegin();
222   for (; ritLine != aLines.rend(); ++ritLine) {
223     // Calculate the start of the line (find the aLineNo occurrence of "\n" )
224     int aLineNo = ritLine->first - 1;
225     size_t aLineStart = 0;
226     for (int i = 0; i < aLineNo; ++i)
227       aLineStart = anExpressionString.find("\n", aLineStart) + 1;
228
229     const std::list<int>& aColOffsets = ritLine->second;
230     std::list<int>::const_reverse_iterator ritOffset = aColOffsets.rbegin();
231     for (; ritOffset != aColOffsets.rend(); ++ritOffset) {
232       int anOffset = *ritOffset;
233       anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName);
234     }
235   }
236
237   return anExpressionString;
238 }
239
240 void ParametersPlugin_EvalListener::renameInParameter(
241     std::shared_ptr<ParametersPlugin_Parameter> theParameter,
242     const std::string& theOldName,
243     const std::string& theNewName)
244 {
245   std::shared_ptr<ModelAPI_AttributeString> anExpressionAttribute =
246       theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID());
247
248   std::string anExpressionString = anExpressionAttribute->value();
249   anExpressionString = renameInPythonExpression(anExpressionString,
250                                                 theOldName,
251                                                 theNewName);
252   // Issue #588. No need for reevaluating expression. 
253   // Moreover, current history may not contain necessary parameters.
254   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(true);
255   anExpressionAttribute->setValue(anExpressionString);
256   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(false);
257 }
258
259 void ParametersPlugin_EvalListener::renameInAttribute(
260     std::shared_ptr<ModelAPI_Attribute> theAttribute,
261     const std::string& theOldName,
262     const std::string& theNewName)
263 {
264   if (theAttribute->attributeType() == ModelAPI_AttributeInteger::typeId()) {
265     AttributeIntegerPtr anAttribute =
266         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
267     std::string anExpressionString = anAttribute->text();
268     anExpressionString = renameInPythonExpression(anExpressionString,
269                                                   theOldName, theNewName);
270     anAttribute->setText(anExpressionString);
271   } else
272   if (theAttribute->attributeType() == ModelAPI_AttributeDouble::typeId()) {
273     AttributeDoublePtr anAttribute =
274         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
275     std::string anExpressionString = anAttribute->text();
276     anExpressionString = renameInPythonExpression(anExpressionString,
277                                                   theOldName, theNewName);
278     anAttribute->setText(anExpressionString);
279   } else
280   if (theAttribute->attributeType() == GeomDataAPI_Point::typeId()) {
281     AttributePointPtr anAttribute =
282         std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
283     std::string anExpressionString[3] = {
284       anAttribute->textX(),
285       anAttribute->textY(),
286       anAttribute->textZ()
287     };
288     for (int i = 0; i < 3; ++i)
289       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
290                                                        theOldName, theNewName);
291     anAttribute->setText(anExpressionString[0],
292                          anExpressionString[1],
293                          anExpressionString[2]);
294   } else
295   if (theAttribute->attributeType() == GeomDataAPI_Point2D::typeId()) {
296     AttributePoint2DPtr anAttribute =
297         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
298     std::string anExpressionString[2] = {
299       anAttribute->textX(),
300       anAttribute->textY()
301     };
302     for (int i = 0; i < 2; ++i)
303       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
304                                                        theOldName, theNewName);
305     anAttribute->setText(anExpressionString[0],
306                          anExpressionString[1]);
307   }
308 }
309
310 void ParametersPlugin_EvalListener::renameInDependents(std::shared_ptr<ModelAPI_ResultParameter> theResultParameter,
311                                                        const std::string& theOldName,
312                                                        const std::string& theNewName)
313 {
314   std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes =
315       theResultParameter->data()->refsToMe();
316   std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator anAttributeIt =
317       anAttributes.cbegin();
318   for (; anAttributeIt != anAttributes.cend(); ++anAttributeIt) {
319     const AttributePtr& anAttribute = *anAttributeIt;
320     if (anAttribute->attributeType() == ModelAPI_AttributeRefList::typeId()) {
321       std::shared_ptr<ParametersPlugin_Parameter> aParameter =
322           std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
323               anAttribute->owner());
324       if (aParameter.get())
325         // Rename
326         renameInParameter(aParameter, theOldName, theNewName);
327     } else
328         // Rename
329         renameInAttribute(anAttribute, theOldName, theNewName);
330   }
331 }
332
333 bool isValidAttribute(const AttributePtr& theAttribute)
334 {
335   std::string aValidator, anError;
336   return ModelAPI_Session::get()->validators()->validate(theAttribute, aValidator, anError);
337 }
338
339 void setParameterName(ResultParameterPtr theResultParameter, const std::string& theName)
340 {
341   theResultParameter->data()->blockSendAttributeUpdated(true);
342   theResultParameter->data()->setName(theName);
343   theResultParameter->data()->blockSendAttributeUpdated(false);
344
345   std::shared_ptr<ParametersPlugin_Parameter> aParameter = 
346       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
347           ModelAPI_Feature::feature(theResultParameter));
348
349   aParameter->data()->blockSendAttributeUpdated(true);
350   aParameter->data()->setName(theName);
351   aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(theName);
352   aParameter->data()->blockSendAttributeUpdated(false);
353 }
354
355 void ParametersPlugin_EvalListener::processObjectRenamedEvent(
356     const std::shared_ptr<Events_Message>& theMessage)
357 {
358   std::shared_ptr<ModelAPI_ObjectRenamedMessage> aMessage =
359       std::dynamic_pointer_cast<ModelAPI_ObjectRenamedMessage>(theMessage);
360
361   // Empty new name is not available too but it will be rejected by
362   // name validator in isValidAttribute.
363   if (!aMessage.get() || aMessage->oldName().empty())
364     return;
365
366   // check if the renamed object is a result parameter
367   ResultParameterPtr aResultParameter =
368       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aMessage->object());
369   if (!aResultParameter.get()) 
370     return;
371
372   // get parameter feature for the result
373   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
374       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
375           ModelAPI_Feature::feature(aResultParameter));
376   if (!aParameter.get())
377     return;
378
379   // try to update the parameter feature according the new name
380   setParameterName(aResultParameter, aMessage->newName());
381   // TODO(spo): replace with ModelAPI_Session::get()->validators()->validate(aParameter, ParametersPlugin_Parameter::VARIABLE_ID())
382   // when ModelAPI_ValidatorsFactory::validate(const std::shared_ptr<ModelAPI_Feature>& theFeature, const std::string& theAttribute) const
383   // is ready
384   if (!isValidAttribute(aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID()))) {
385     setParameterName(aResultParameter, aMessage->oldName());
386     return;
387   }
388
389   renameInDependents(aResultParameter, aMessage->oldName(), aMessage->newName());
390 }
391
392 void ParametersPlugin_EvalListener::processReplaceParameterEvent(
393     const std::shared_ptr<Events_Message>& theMessage)
394 {
395   std::shared_ptr<ModelAPI_ReplaceParameterMessage> aMessage =
396       std::dynamic_pointer_cast<ModelAPI_ReplaceParameterMessage>(theMessage);
397
398   // get parameter feature for the object
399   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
400       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
401           ModelAPI_Feature::feature(aMessage->object()));
402   if (!aParameter.get())
403     return;
404
405   ResultParameterPtr aResultParameter =
406       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(
407           aParameter->firstResult());
408   if (!aResultParameter.get())
409     return;
410
411   double aRealValue = aResultParameter->data()->real(ModelAPI_ResultParameter::VALUE())->value();
412   std::string aValue = toStdString(aRealValue);
413
414   renameInDependents(aResultParameter, aResultParameter->data()->name(), aValue);
415 }