Salome HOME
b5acdb33e644bbc8cd633950b5f13414150be821
[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_AttributeString.h>
17 #include <ModelAPI_Document.h>
18 #include <ModelAPI_Events.h>
19 #include <ModelAPI_Session.h>
20 #include <ModelAPI_Tools.h>
21 #include <ModelAPI_AttributeValidator.h>
22
23 #include <ModelAPI_AttributeDouble.h>
24 #include <GeomDataAPI_Point.h>
25 #include <GeomDataAPI_Point2D.h>
26
27 #include <string>
28 #include <set>
29 #include <sstream>
30
31 ParametersPlugin_EvalListener::ParametersPlugin_EvalListener()
32 {
33   Events_Loop* aLoop = Events_Loop::loop();
34   const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId();
35   aLoop->registerListener(this, kEvaluationEvent, NULL, true);
36   const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId();
37   aLoop->registerListener(this, kObjectRenamedEvent, NULL, true);
38
39   myInterp = std::shared_ptr<ParametersPlugin_PyInterp>(new ParametersPlugin_PyInterp());
40   myInterp->initialize();
41 }
42
43 ParametersPlugin_EvalListener::~ParametersPlugin_EvalListener()
44 {
45 }
46
47 void ParametersPlugin_EvalListener::processEvent(
48     const std::shared_ptr<Events_Message>& theMessage)
49 {
50   if (!theMessage.get())
51     return;
52
53   const Events_ID kEvaluationEvent = ModelAPI_AttributeEvalMessage::eventId();
54   const Events_ID kObjectRenamedEvent = ModelAPI_ObjectRenamedMessage::eventId();
55   if (theMessage->eventID() == kEvaluationEvent) {
56     processEvaluationEvent(theMessage);
57   } else if (theMessage->eventID() == kObjectRenamedEvent) {
58     processObjectRenamedEvent(theMessage);
59   } else {
60     Events_Error::send(std::string("ParametersPlugin python interpreter, unhandled message caught: ")
61                        + theMessage->eventID().eventText());
62   }
63 }
64
65 double ParametersPlugin_EvalListener::evaluate(const std::string& theExpression,
66                                                std::string& theError)
67 {
68   std::list<std::string> anExprParams = myInterp->compile(theExpression);
69   // find expression's params in the model
70   std::list<std::string> aContext;
71   std::list<std::string>::iterator it = anExprParams.begin();
72   for ( ; it != anExprParams.end(); it++) {
73     double aValue;
74     ResultParameterPtr aParamRes;
75     if (!ModelAPI_Tools::findVariable(*it, aValue, aParamRes)) continue;
76
77     std::ostringstream sstream;
78     sstream << aValue;
79     std::string aParamValue = sstream.str();
80     aContext.push_back(*it + "=" + aParamValue);
81   }
82   myInterp->extendLocalContext(aContext);
83   double result = myInterp->evaluate(theExpression, theError);
84   myInterp->clearLocalContext();
85   return result;
86 }
87
88 std::set<std::string> toSet(const std::list<std::string>& theContainer)
89 {
90   return std::set<std::string>(theContainer.begin(), theContainer.end());
91 }
92
93 void ParametersPlugin_EvalListener::processEvaluationEvent(
94     const std::shared_ptr<Events_Message>& theMessage)
95 {
96   std::shared_ptr<ModelAPI_AttributeEvalMessage> aMessage =
97       std::dynamic_pointer_cast<ModelAPI_AttributeEvalMessage>(theMessage);
98
99   if (aMessage->attribute()->attributeType() == ModelAPI_AttributeDouble::typeId()) {
100     AttributeDoublePtr anAttribute =
101         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(aMessage->attribute());
102     std::string anError;
103     double aValue = evaluate(anAttribute->text(), anError);
104     bool isValid = anError.empty();
105     if (isValid)
106       anAttribute->setCalculatedValue(aValue);
107     anAttribute->setUsedParameters(isValid ? toSet(myInterp->compile(anAttribute->text())) : std::set<std::string>());
108     anAttribute->setExpressionInvalid(!isValid);
109     anAttribute->setExpressionError(anAttribute->text().empty() ? "" : anError);
110   } else
111   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point::typeId()) {
112     AttributePointPtr anAttribute =
113         std::dynamic_pointer_cast<GeomDataAPI_Point>(aMessage->attribute());
114     std::string aText[] = {
115       anAttribute->textX(),
116       anAttribute->textY(),
117       anAttribute->textZ()
118     };
119     double aCalculatedValue[] = {
120       anAttribute->x(),
121       anAttribute->y(),
122       anAttribute->z()
123     };
124     for (int i = 0; i < 3; ++i) {
125       std::string anError;
126       double aValue = evaluate(aText[i], anError);
127       bool isValid = anError.empty();
128       if (isValid) aCalculatedValue[i] = aValue;
129       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
130       anAttribute->setExpressionInvalid(i, !isValid);
131       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
132     }
133     anAttribute->setCalculatedValue(aCalculatedValue[0],
134                                     aCalculatedValue[1],
135                                     aCalculatedValue[2]);
136   } else
137   if (aMessage->attribute()->attributeType() == GeomDataAPI_Point2D::typeId()) {
138     AttributePoint2DPtr anAttribute =
139         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMessage->attribute());
140     std::string aText[] = {
141       anAttribute->textX(),
142       anAttribute->textY()
143     };
144     double aCalculatedValue[] = {
145       anAttribute->x(),
146       anAttribute->y()
147     };
148     for (int i = 0; i < 2; ++i) {
149       std::string anError;
150       double aValue = evaluate(aText[i], anError);
151       bool isValid = anError.empty();
152       if (isValid) aCalculatedValue[i] = aValue;
153       anAttribute->setUsedParameters(i, isValid ? toSet(myInterp->compile(aText[i])) : std::set<std::string>());
154       anAttribute->setExpressionInvalid(i, !isValid);
155       anAttribute->setExpressionError(i, aText[i].empty() ? "" : anError);
156     }
157     anAttribute->setCalculatedValue(aCalculatedValue[0],
158                                     aCalculatedValue[1]);
159   }
160 }
161
162 std::string ParametersPlugin_EvalListener::renameInPythonExpression(
163     const std::string& theExpression,
164     const std::string& theOldName,
165     const std::string& theNewName)
166 {
167   std::string anExpressionString = theExpression;
168
169   std::list<std::pair<int, int> > aPositions =
170       myInterp->positions(anExpressionString, theOldName);
171
172   if (aPositions.empty())
173     return anExpressionString;
174
175   std::map<int, std::list<int> > aLines;
176   std::list<std::pair<int, int> >::const_iterator it = aPositions.begin();
177   for (; it != aPositions.end(); ++it)
178     aLines[it->first].push_back(it->second);
179
180   // Start renaming from the end to keep indexes if theNewName is longer then theOldName
181   std::map<int, std::list<int> >::const_reverse_iterator ritLine = aLines.rbegin();
182   for (; ritLine != aLines.rend(); ++ritLine) {
183     // Calculate the start of the line (find the aLineNo occurrence of "\n" )
184     int aLineNo = ritLine->first - 1;
185     size_t aLineStart = 0;
186     for (int i = 0; i < aLineNo; ++i)
187       aLineStart = anExpressionString.find("\n", aLineStart) + 1;
188
189     const std::list<int>& aColOffsets = ritLine->second;
190     std::list<int>::const_reverse_iterator ritOffset = aColOffsets.rbegin();
191     for (; ritOffset != aColOffsets.rend(); ++ritOffset) {
192       int anOffset = *ritOffset;
193       anExpressionString.replace(aLineStart + anOffset, theOldName.size(), theNewName);
194     }
195   }
196
197   return anExpressionString;
198 }
199
200 void ParametersPlugin_EvalListener::renameInParameter(
201     std::shared_ptr<ParametersPlugin_Parameter> theParameter,
202     const std::string& theOldName,
203     const std::string& theNewName)
204 {
205   std::shared_ptr<ModelAPI_AttributeString> anExpressionAttribute =
206       theParameter->string(ParametersPlugin_Parameter::EXPRESSION_ID());
207
208   std::string anExpressionString = anExpressionAttribute->value();
209   anExpressionString = renameInPythonExpression(anExpressionString,
210                                                 theOldName,
211                                                 theNewName);
212   // Issue #588. No need for reevaluating expression. 
213   // Moreover, current history may not contain necessary parameters.
214   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(true);
215   anExpressionAttribute->setValue(anExpressionString);
216   anExpressionAttribute->owner()->data()->blockSendAttributeUpdated(false);
217 }
218
219 void ParametersPlugin_EvalListener::renameInAttribute(
220     std::shared_ptr<ModelAPI_Attribute> theAttribute,
221     const std::string& theOldName,
222     const std::string& theNewName)
223 {
224   if (theAttribute->attributeType() == ModelAPI_AttributeDouble::typeId()) {
225     AttributeDoublePtr anAttribute =
226         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
227     std::string anExpressionString = anAttribute->text();
228     anExpressionString = renameInPythonExpression(anExpressionString,
229                                                   theOldName, theNewName);
230     anAttribute->setText(anExpressionString);
231   } else
232   if (theAttribute->attributeType() == GeomDataAPI_Point::typeId()) {
233     AttributePointPtr anAttribute =
234         std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
235     std::string anExpressionString[3] = {
236       anAttribute->textX(),
237       anAttribute->textY(),
238       anAttribute->textZ()
239     };
240     for (int i = 0; i < 3; ++i)
241       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
242                                                        theOldName, theNewName);
243     anAttribute->setText(anExpressionString[0],
244                          anExpressionString[1],
245                          anExpressionString[2]);
246   } else
247   if (theAttribute->attributeType() == GeomDataAPI_Point2D::typeId()) {
248     AttributePoint2DPtr anAttribute =
249         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
250     std::string anExpressionString[2] = {
251       anAttribute->textX(),
252       anAttribute->textY()
253     };
254     for (int i = 0; i < 2; ++i)
255       anExpressionString[i] = renameInPythonExpression(anExpressionString[i],
256                                                        theOldName, theNewName);
257     anAttribute->setText(anExpressionString[0],
258                          anExpressionString[1]);
259   }
260 }
261
262 bool isValidAttribute(const AttributePtr& theAttribute)
263 {
264   std::string aValidator, anError;
265   return ModelAPI_Session::get()->validators()->validate(theAttribute, aValidator, anError);
266 }
267
268 void setParameterName(ResultParameterPtr theResultParameter, const std::string& theName)
269 {
270   theResultParameter->data()->blockSendAttributeUpdated(true);
271   theResultParameter->data()->setName(theName);
272   theResultParameter->data()->blockSendAttributeUpdated(false);
273
274   std::shared_ptr<ParametersPlugin_Parameter> aParameter = 
275       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
276           ModelAPI_Feature::feature(theResultParameter));
277
278   aParameter->data()->blockSendAttributeUpdated(true);
279   aParameter->data()->setName(theName);
280   aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID())->setValue(theName);
281   aParameter->data()->blockSendAttributeUpdated(false);
282 }
283
284 void ParametersPlugin_EvalListener::processObjectRenamedEvent(
285     const std::shared_ptr<Events_Message>& theMessage)
286 {
287   std::shared_ptr<ModelAPI_ObjectRenamedMessage> aMessage =
288       std::dynamic_pointer_cast<ModelAPI_ObjectRenamedMessage>(theMessage);
289
290   if (!aMessage.get() || aMessage->oldName().empty() || aMessage->newName().empty())
291     return;
292
293   // check if the renamed object is a result parameter
294   ResultParameterPtr aResultParameter =
295       std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aMessage->object());
296   if (!aResultParameter.get()) 
297     return;
298
299   // get parameter feature for the result
300   std::shared_ptr<ParametersPlugin_Parameter> aParameter =
301       std::dynamic_pointer_cast<ParametersPlugin_Parameter>(
302           ModelAPI_Feature::feature(aResultParameter));
303   if (!aParameter.get())
304     return;
305
306   // try to update the parameter feature according the new name
307   setParameterName(aResultParameter, aMessage->newName());
308   // TODO(spo): replace with ModelAPI_Session::get()->validators()->validate(aParameter, ParametersPlugin_Parameter::VARIABLE_ID())
309   // when ModelAPI_ValidatorsFactory::validate(const std::shared_ptr<ModelAPI_Feature>& theFeature, const std::string& theAttribute) const
310   // is ready
311   if (!isValidAttribute(aParameter->string(ParametersPlugin_Parameter::VARIABLE_ID()))) {
312     setParameterName(aResultParameter, aMessage->oldName());
313     return;
314   }
315
316   // List of documents to process
317   std::list<DocumentPtr> aDocList;
318   SessionPtr aSession = ModelAPI_Session::get();
319   DocumentPtr aDocument = aSession->activeDocument();
320   DocumentPtr aRootDocument = aSession->moduleDocument();
321   aDocList.push_back(aDocument);
322   if (aDocument != aRootDocument) {
323     aDocList.push_back(aRootDocument);
324   }
325
326   // Find all features
327   for (std::list<DocumentPtr>::const_iterator aDicumentIt = aDocList.begin(); 
328        aDicumentIt != aDocList.end(); ++aDicumentIt) {
329     const DocumentPtr& aDocument = *aDicumentIt;
330     std::list<FeaturePtr> aFeatures = aDocument->allFeatures();
331     std::list<FeaturePtr>::iterator aFeatureIt = aFeatures.begin();
332     for (; aFeatureIt != aFeatures.end(); ++aFeatureIt) {
333       const FeaturePtr& aFeature = *aFeatureIt;
334       
335       // If Parameter feature then rename its expression
336       std::shared_ptr<ParametersPlugin_Parameter> aParameter =
337           std::dynamic_pointer_cast<ParametersPlugin_Parameter>(aFeature);
338       if (aParameter.get()) {
339         // Rename
340         renameInParameter(aParameter, aMessage->oldName(), aMessage->newName());
341         continue;
342       }      
343
344       // Find all attributes
345       std::list<AttributePtr> anAttributes = aFeature->data()->attributes(std::string());
346       std::list<AttributePtr>::const_iterator anAttributeIt = anAttributes.begin();
347       for (; anAttributeIt != anAttributes.end(); ++anAttributeIt) {
348         const AttributePtr& anAttribute = *anAttributeIt;
349
350         // Rename
351         renameInAttribute(anAttribute, aMessage->oldName(), aMessage->newName());
352       }
353     }
354   }
355 }
356