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