Salome HOME
Issue #1472: Represent numerical value as real value in a string
[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(const std::string& theExpression, std::string& theError, 
98                                                const std::shared_ptr<ModelAPI_Document>& theDocument)
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(*it, aValue, aParamRes, theDocument)) continue;
109
110     aContext.push_back(*it + "=" + toStdString(aValue));
111   }
112   myInterp->extendLocalContext(aContext);
113   double result = myInterp->evaluate(theExpression, theError);
114   myInterp->clearLocalContext();
115   return result;
116 }
117
118 void ParametersPlugin_EvalListener::processEvaluationEvent(
119     const std::shared_ptr<Events_Message>& theMessage)
120 {
121   std::shared_ptr<ModelAPI_AttributeEvalMessage> aMessage =
122       std::dynamic_pointer_cast<ModelAPI_AttributeEvalMessage>(theMessage);
123
124   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeInteger::typeId()) {
125     AttributeIntegerPtr anAttribute =
126         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(aMessage->attribute());
127     std::string anError;
128     int aValue = (int)evaluate(anAttribute->text(), anError, anAttribute->owner()->document());
129     bool isValid = anError.empty();
130     if (isValid)
131       anAttribute->setCalculatedValue(aValue);
132     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
133     anAttribute->setExpressionInvalid(!isValid);
134     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
135   } else
136   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) {
137     AttributeDoublePtr anAttribute =
138         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aMessage->attribute());
139     std::string anError;
140     double aValue = evaluate(anAttribute->text(), anError, anAttribute->owner()->document());
141     bool isValid = anError.empty();
142     if (isValid)
143       anAttribute->setCalculatedValue(aValue);
144     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
145     anAttribute->setExpressionInvalid(!isValid);
146     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
147   } else
148   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) {
149     AttributePointPtr anAttribute =
150         std::dynamic_pointer_cast<GeomDataAPI_Point>(aMessage->attribute());
151     std::string aText[] = {
152       anAttribute->textX(),
153       anAttribute->textY(),
154       anAttribute->textZ()
155     };
156     double aCalculatedValue[] = {
157       anAttribute->x(),
158       anAttribute->y(),
159       anAttribute->z()
160     };
161     for (int i = 0; i < 3; ++i) {
162       std::string anError;
163       double aValue = evaluate(aText[i], anError, anAttribute->owner()->document());
164       bool isValid = anError.empty();
165       if (isValid) aCalculatedValue[i] = aValue;
166       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
167       anAttribute->setExpressionInvalid(i, !isValid);
168       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
169     }
170     anAttribute->setCalculatedValue(aCalculatedValue[0],
171                                     aCalculatedValue[1],
172                                     aCalculatedValue[2]);
173   } else
174   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) {
175     AttributePoint2DPtr anAttribute =
176         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMessage->attribute());
177     std::string aText[] = {
178       anAttribute->textX(),
179       anAttribute->textY()
180     };
181     double aCalculatedValue[] = {
182       anAttribute->x(),
183       anAttribute->y()
184     };
185     for (int i = 0; i < 2; ++i) {
186       std::string anError;
187       double aValue = evaluate(aText[i], anError, anAttribute->owner()->document());
188       bool isValid = anError.empty();
189       if (isValid) aCalculatedValue[i] = aValue;
190       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
191       anAttribute->setExpressionInvalid(i, !isValid);
192       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
193     }
194     anAttribute->setCalculatedValue(aCalculatedValue[0],
195                                     aCalculatedValue[1]);
196   }
197 }
198
199 std::string ParametersPlugin_EvalListener::renameInPythonExpression(
200     const std::string& theExpression,
201     const std::string& theOldName,
202     const std::string& theNewName)
203 {
204   std::string anExpressionString = theExpression;
205
206   std::list<std::pair<int, int> > aPositions =
207       myInterp->positions(anExpressionString, theOldName);
208
209   if (aPositions.empty())
210     return anExpressionString;
211
212   std::map<int, std::list<int> > aLines;
213   std::list<std::pair<int, int> >::const_iterator it = aPositions.begin();
214   for (; it != aPositions.end(); ++it)
215     aLines[it->first].push_back(it->second);
216
217   // Start renaming from the end to keep indexes if theNewName is longer then theOldName
218   std::map<int, std::list<int> >::const_reverse_iterator ritLine = aLines.rbegin();
219   for (; ritLine != aLines.rend(); ++ritLine) {
220     // Calculate the start of the line (find the aLineNo occurrence of "\n" )
221     int aLineNo = ritLine->first - 1;
222     size_t aLineStart = 0;
223     for (int i = 0; i < aLineNo; ++i)
224       aLineStart = anExpressionString.find("\n", aLineStart) + 1;
225
226     const std::list<int>& aColOffsets = ritLine->second;
227     std::list<int>::const_reverse_iterator ritOffset = aColOffsets.rbegin();
228     for (; ritOffset != aColOffsets.rend(); ++ritOffset) {
229       int anOffset = *ritOffset;
230       anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName);
231     }
232   }
233
234   return anExpressionString;
235 }
236
237 void ParametersPlugin_EvalListener::renameInParameter(
238     std::shared_ptr<ParametersPlugin_Parameter> theParameter,
239     const std::string& theOldName,
240     const std::string& theNewName)
241 {
242   std::shared_ptr<ModelAPI_AttributeString> anExpressionAttribute =
243       theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID());
244
245   std::string anExpressionString = anExpressionAttribute->value();
246   anExpressionString = renameInPythonExpression(anExpressionString,
247                                                 theOldName,
248                                                 theNewName);
249   // Issue #588. No need for reevaluating expression. 
250   // Moreover, current history may not contain necessary parameters.
251   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(true);
252   anExpressionAttribute->setValue(anExpressionString);
253   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(false);
254 }
255
256 void ParametersPlugin_EvalListener::renameInAttribute(
257     std::shared_ptr<ModelAPI_Attribute> theAttribute,
258     const std::string& theOldName,
259     const std::string& theNewName)
260 {
261   if (theAttribute->attributeType() == ModelAPI_AttributeInteger::typeId()) {
262     AttributeIntegerPtr anAttribute =
263         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
264     std::string anExpressionString = anAttribute->text();
265     anExpressionString = renameInPythonExpression(anExpressionString,
266                                                   theOldName, theNewName);
267     anAttribute->setText(anExpressionString);
268   } else
269   if (theAttribute->attributeType() == ModelAPI_AttributeDouble::typeId()) {
270     AttributeDoublePtr anAttribute =
271         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
272     std::string anExpressionString = anAttribute->text();
273     anExpressionString = renameInPythonExpression(anExpressionString,
274                                                   theOldName, theNewName);
275     anAttribute->setText(anExpressionString);
276   } else
277   if (theAttribute->attributeType() == GeomDataAPI_Point::typeId()) {
278     AttributePointPtr anAttribute =
279         std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
280     std::string anExpressionString[3] = {
281       anAttribute->textX(),
282       anAttribute->textY(),
283       anAttribute->textZ()
284     };
285     for (int i = 0; i < 3; ++i)
286       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
287                                                        theOldName, theNewName);
288     anAttribute->setText(anExpressionString[0],
289                          anExpressionString[1],
290                          anExpressionString[2]);
291   } else
292   if (theAttribute->attributeType() == GeomDataAPI_Point2D::typeId()) {
293     AttributePoint2DPtr anAttribute =
294         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
295     std::string anExpressionString[2] = {
296       anAttribute->textX(),
297       anAttribute->textY()
298     };
299     for (int i = 0; i < 2; ++i)
300       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
301                                                        theOldName, theNewName);
302     anAttribute->setText(anExpressionString[0],
303                          anExpressionString[1]);
304   }
305 }
306
307 void ParametersPlugin_EvalListener::renameInDependents(std::shared_ptr<ModelAPI_ResultParameter> theResultParameter,
308                                                        const std::string& theOldName,
309                                                        const std::string& theNewName)
310 {
311   std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes =
312       theResultParameter->data()->refsToMe();
313   std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator anAttributeIt =
314       anAttributes.cbegin();
315   for (; anAttributeIt != anAttributes.cend(); ++anAttributeIt) {
316     const AttributePtr& anAttribute = *anAttributeIt;
317     if (anAttribute->attributeType() == ModelAPI_AttributeRefList::typeId()) {
318       std::shared_ptr<ParametersPlugin_Parameter> aParameter =
319           std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
320               anAttribute->owner());
321       if (aParameter.get())
322         // Rename
323         renameInParameter(aParameter, theOldName, theNewName);
324     } else
325         // Rename
326         renameInAttribute(anAttribute, theOldName, theNewName);
327   }
328 }
329
330 bool isValidAttribute(const AttributePtr& theAttribute)
331 {
332   std::string aValidator, anError;
333   return ModelAPI_Session::get()->validators()->validate(theAttribute, aValidator, anError);
334 }
335
336 void setParameterName(ResultParameterPtr theResultParameter, const std::string& theName)
337 {
338   theResultParameter->data()->blockSendAttributeUpdated(true);
339   theResultParameter->data()->setName(theName);
340   theResultParameter->data()->blockSendAttributeUpdated(false);
341
342   std::shared_ptr<ParametersPlugin_Parameter> aParameter = 
343       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
344           ModelAPI_Feature::feature(theResultParameter));
345
346   aParameter->data()->blockSendAttributeUpdated(true);
347   aParameter->data()->setName(theName);
348   aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(theName);
349   aParameter->data()->blockSendAttributeUpdated(false);
350 }
351
352 void ParametersPlugin_EvalListener::processObjectRenamedEvent(
353     const std::shared_ptr<Events_Message>& theMessage)
354 {
355   std::shared_ptr<ModelAPI_ObjectRenamedMessage> aMessage =
356       std::dynamic_pointer_cast<ModelAPI_ObjectRenamedMessage>(theMessage);
357
358   // Empty new name is not available too but it will be rejected by
359   // name validator in isValidAttribute.
360   if (!aMessage.get() || aMessage->oldName().empty())
361     return;
362
363   // check if the renamed object is a result parameter
364   ResultParameterPtr aResultParameter =
365       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aMessage->object());
366   if (!aResultParameter.get()) 
367     return;
368
369   // get parameter feature for the result
370   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
371       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
372           ModelAPI_Feature::feature(aResultParameter));
373   if (!aParameter.get())
374     return;
375
376   // try to update the parameter feature according the new name
377   setParameterName(aResultParameter, aMessage->newName());
378   // TODO(spo): replace with ModelAPI_Session::get()->validators()->validate(aParameter, ParametersPlugin_Parameter::VARIABLE_ID())
379   // when ModelAPI_ValidatorsFactory::validate(const std::shared_ptr<ModelAPI_Feature>& theFeature, const std::string& theAttribute) const
380   // is ready
381   if (!isValidAttribute(aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID()))) {
382     setParameterName(aResultParameter, aMessage->oldName());
383     return;
384   }
385
386   renameInDependents(aResultParameter, aMessage->oldName(), aMessage->newName());
387 }
388
389 void ParametersPlugin_EvalListener::processReplaceParameterEvent(
390     const std::shared_ptr<Events_Message>& theMessage)
391 {
392   std::shared_ptr<ModelAPI_ReplaceParameterMessage> aMessage =
393       std::dynamic_pointer_cast<ModelAPI_ReplaceParameterMessage>(theMessage);
394
395   // get parameter feature for the object
396   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
397       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
398           ModelAPI_Feature::feature(aMessage->object()));
399   if (!aParameter.get())
400     return;
401
402   ResultParameterPtr aResultParameter =
403       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(
404           aParameter->firstResult());
405   if (!aResultParameter.get())
406     return;
407
408   double aRealValue = aResultParameter->data()->real(ModelAPI_ResultParameter::VALUE())->value();
409   std::string aValue = toStdString(aRealValue);
410
411   renameInDependents(aResultParameter, aResultParameter->data()->name(), aValue);
412 }