1 // File: Model_Update.cxx
2 // Created: 25 Jun 2014
3 // Author: Mikhail PONIKAROV
5 #include <Model_Update.h>
6 #include <Model_Document.h>
7 #include <ModelAPI_Feature.h>
8 #include <ModelAPI_Data.h>
9 #include <ModelAPI_Document.h>
10 #include <ModelAPI_Events.h>
11 #include <ModelAPI_AttributeReference.h>
12 #include <ModelAPI_AttributeRefList.h>
13 #include <ModelAPI_AttributeRefAttr.h>
14 #include <ModelAPI_AttributeSelection.h>
15 #include <ModelAPI_AttributeSelectionList.h>
16 #include <ModelAPI_Result.h>
17 #include <ModelAPI_Validator.h>
18 #include <ModelAPI_CompositeFeature.h>
19 #include <Events_Loop.h>
20 #include <Events_LongOp.h>
21 #include <Events_Error.h>
22 #include <Config_PropManager.h>
26 Model_Update MY_UPDATER_INSTANCE; /// the only one instance initialized on load of the library
28 Model_Update::Model_Update()
30 Events_Loop* aLoop = Events_Loop::loop();
31 static const Events_ID kChangedEvent = aLoop->eventByName("PreferenceChanged");
32 aLoop->registerListener(this, kChangedEvent);
33 static const Events_ID kRebuildEvent = aLoop->eventByName("Rebuild");
34 aLoop->registerListener(this, kRebuildEvent);
35 static const Events_ID kCreatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED);
36 aLoop->registerListener(this, kCreatedEvent);
37 static const Events_ID kUpdatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED);
38 aLoop->registerListener(this, kUpdatedEvent);
39 static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
40 aLoop->registerListener(this, kOpFinishEvent);
41 static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
42 aLoop->registerListener(this, kOpAbortEvent);
44 Config_PropManager::registerProp("Model update", "automatic_rebuild", "Rebuild automatically",
45 Config_Prop::Bool, "false");
46 isAutomatic = Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
49 void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMessage)
51 static Events_Loop* aLoop = Events_Loop::loop();
52 static const Events_ID kChangedEvent = aLoop->eventByName("PreferenceChanged");
53 static const Events_ID kRebuildEvent = aLoop->eventByName("Rebuild");
54 static const Events_ID kCreatedEvent = aLoop->eventByName(EVENT_OBJECT_CREATED);
55 static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
56 static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
57 static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
58 bool isAutomaticChanged = false;
59 if (theMessage->eventID() == kChangedEvent) { // automatic and manual rebuild flag is changed
61 Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
62 } else if (theMessage->eventID() == kRebuildEvent) { // the rebuild command
63 if (isAutomatic == false) {
64 isAutomaticChanged = true;
67 } else if (theMessage->eventID() == kCreatedEvent || theMessage->eventID() == kUpdatedEvent) {
68 boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
69 boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
70 const std::set<ObjectPtr>& anObjs = aMsg->objects();
71 std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
72 for(; anObjIter != anObjs.cend(); anObjIter++)
73 myJustCreatedOrUpdated.insert(*anObjIter);
74 } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
75 myJustCreatedOrUpdated.clear();
80 return; // nothing to do: it is executed now
82 //Events_LongOp::start(this);
84 list<boost::shared_ptr<ModelAPI_Document> > aDocs;
85 boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
86 boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
87 if (aMsg) myInitial = aMsg->objects();
90 // on change flag all documents must be updated
92 aDocs = ModelAPI_Session::get()->allOpenedDocuments();
95 // collect all documents involved into the update process
96 set<boost::shared_ptr<ModelAPI_Object> >::iterator aFIter = myInitial.begin();
97 for (; aFIter != myInitial.end(); aFIter++) {
98 aDocs.push_back((*aFIter)->document());
100 // iterate all features of features-documents to update them (including hidden)
101 list<boost::shared_ptr<ModelAPI_Document> >::iterator aDIter = aDocs.begin();
102 for (; aDIter != aDocs.end(); aDIter++) {
103 int aNbFeatures = (*aDIter)->size(ModelAPI_Feature::group(), true);
104 for (int aFIndex = 0; aFIndex < aNbFeatures; aFIndex++) {
105 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(
106 (*aDIter)->object(ModelAPI_Feature::group(), aFIndex, true));
108 updateFeature(aFeature);
113 static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
114 aLoop->flush(EVENT_DISP);
115 //Events_LongOp::end(this);
116 if (isAutomaticChanged) isAutomatic = false;
120 bool Model_Update::updateFeature(FeaturePtr theFeature)
122 // check it is already processed
123 if (myUpdated.find(theFeature) != myUpdated.end())
124 return myUpdated[theFeature];
125 // check all features this feature depended on (recursive call of updateFeature)
126 bool aMustbeUpdated = myInitial.find(theFeature) != myInitial.end();
127 if (theFeature) { // only real feature contains references to other objects
128 if (theFeature->data()->mustBeUpdated()) aMustbeUpdated = true;
130 // composite feature must be executed after sub-features execution
131 CompositeFeaturePtr aComposite =
132 boost::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
134 int aSubsNum = aComposite->numberOfSubs();
135 for(int a = 0; a < aSubsNum; a++) {
136 if (updateFeature(aComposite->subFeature(a)))
137 aMustbeUpdated = true;
142 list<boost::shared_ptr<ModelAPI_Attribute> > aRefs = theFeature->data()->attributes(
143 ModelAPI_AttributeReference::type());
144 list<boost::shared_ptr<ModelAPI_Attribute> >::iterator aRefsIter = aRefs.begin();
145 for (; aRefsIter != aRefs.end(); aRefsIter++) {
146 boost::shared_ptr<ModelAPI_Object> aSub = boost::dynamic_pointer_cast<
147 ModelAPI_AttributeReference>(*aRefsIter)->value();
148 if (updateObject(aSub)) {
149 aMustbeUpdated = true;
152 // reference to attribute or object
153 list<boost::shared_ptr<ModelAPI_Attribute> > aRefAttrs = theFeature->data()->attributes(
154 ModelAPI_AttributeRefAttr::type());
155 for (aRefsIter = aRefAttrs.begin(); aRefsIter != aRefAttrs.end(); aRefsIter++) {
156 boost::shared_ptr<ModelAPI_AttributeRefAttr> aRef =
157 boost::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefsIter);
159 if (aRef->isObject()) {
160 boost::shared_ptr<ModelAPI_Object> aSub = aRef->object();
161 if (updateObject(aSub)) {
162 aMustbeUpdated = true;
164 } else if (aRef->attr()) { // reference to the attribute
165 boost::shared_ptr<ModelAPI_Object> aSub = aRef->attr()->owner();
166 if (updateObject(aSub)) {
167 aMustbeUpdated = true;
171 // lists of references
172 aRefs = theFeature->data()->attributes(ModelAPI_AttributeRefList::type());
173 for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
174 list<ObjectPtr> aListRef = boost::dynamic_pointer_cast<ModelAPI_AttributeRefList>(*aRefsIter)
176 list<ObjectPtr>::iterator aListIter = aListRef.begin();
177 for (; aListIter != aListRef.end(); aListIter++) {
178 boost::shared_ptr<ModelAPI_Object> aSub = *aListIter;
179 if (updateObject(aSub)) {
180 aMustbeUpdated = true;
184 // selection attributes: must be called "update" methods if needed
185 aRefs = theFeature->data()->attributes(ModelAPI_AttributeSelection::type());
186 for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
187 boost::shared_ptr<ModelAPI_AttributeSelection> aSel =
188 boost::dynamic_pointer_cast<ModelAPI_AttributeSelection>(*aRefsIter);
189 if (updateObject(aSel->context())) {
190 aMustbeUpdated = true;
191 // aSel->update(); // this must be done on execution since it may be long operation
194 // lists of selection attributes: must be called "update" methods if needed
195 aRefs = theFeature->data()->attributes(ModelAPI_AttributeSelectionList::type());
196 for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
197 boost::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
198 boost::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
199 for(int a = aSel->size() - 1; a >= 0; a--) {
200 if (updateObject(aSel->value(a)->context())) {
201 aMustbeUpdated = true;
206 // execute feature if it must be updated
207 if (aMustbeUpdated) {
209 if (boost::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures() ||
210 !theFeature->isPersistentResult()) {
211 ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
212 if (aFactory->validate(theFeature)) {
213 if (isAutomatic || (myJustCreatedOrUpdated.find(theFeature) != myJustCreatedOrUpdated.end()) ||
214 !theFeature->isPersistentResult() /* execute quick, not persistent results */)
216 // before execution update the selection attributes if any
217 aRefs = theFeature->data()->attributes(ModelAPI_AttributeSelection::type());
218 for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
219 boost::shared_ptr<ModelAPI_AttributeSelection> aSel =
220 boost::dynamic_pointer_cast<ModelAPI_AttributeSelection>(*aRefsIter);
221 aSel->update(); // this must be done on execution since it may be long operation
223 aRefs = theFeature->data()->attributes(ModelAPI_AttributeSelectionList::type());
224 for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
225 boost::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
226 boost::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
227 for(int a = aSel->size() - 1; a >= 0; a--) {
228 aSel->value(a)->update();
231 // execute in try-catch to avoid internal problems of the feature
233 theFeature->execute();
236 "Feature " + theFeature->getKind() + " has failed during the execution");
237 theFeature->eraseResults();
239 theFeature->data()->mustBeUpdated(false);
240 const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
241 std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
242 for (; aRIter != aResults.cend(); aRIter++) {
243 boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
244 aRes->data()->mustBeUpdated(false);
247 theFeature->data()->mustBeUpdated(true);
248 const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
249 std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
250 for (; aRIter != aResults.cend(); aRIter++) {
251 boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
252 aRes->data()->mustBeUpdated(true);
254 aMustbeUpdated = false;
257 theFeature->eraseResults();
260 // redisplay all results
261 static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
262 const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
263 std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
264 for (; aRIter != aResults.cend(); aRIter++) {
265 boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
266 myUpdated[aRes] = true;
267 ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
269 // to redisplay "presentable" feature (for ex. distance constraint)
270 ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
271 } else { // returns also true is results were updated: for sketch that refers to sub-features but results of sub-features were changed
272 const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
273 std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
274 for (; aRIter != aResults.cend(); aRIter++) {
275 if (myInitial.find(*aRIter) != myInitial.end()) {
276 aMustbeUpdated = true;
282 myUpdated[theFeature] = aMustbeUpdated;
283 return aMustbeUpdated;
286 bool Model_Update::updateObject(boost::shared_ptr<ModelAPI_Object> theObject)
288 if (myUpdated.find(theObject) != myUpdated.end())
289 return myUpdated[theObject]; // already processed
290 return myInitial.find(theObject) != myInitial.end();
292 /* remove algorithm for update of all features by dependencies tree
295 FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
296 if (aFeature) { // for feature just call update Feature
297 return updateFeature(aFeature);
299 // check general object, possible just a result
300 if (myUpdated.find(theObject) != myUpdated.end())
301 return myUpdated[theObject]; // already processed
302 // check the feature of this object must be executed
303 ResultPtr aResult = boost::dynamic_pointer_cast<ModelAPI_Result>(theObject);
305 FeaturePtr aResFeature = aResult->document()->feature(aResult);
307 return updateFeature(aResFeature);
310 if (myInitial.find(theObject) != myInitial.end())
312 return false; // nothing is known