]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Objects.cpp
Salome HOME
Created a new event that features were reordered due to the "move to end" action
[modules/shaper.git] / src / Model / Model_Objects.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_Objects.cxx
4 // Created:     15 May 2015
5 // Author:      Mikhail PONIKAROV
6
7 #include <Model_Objects.h>
8 #include <Model_Data.h>
9 #include <Model_Document.h>
10 #include <Model_Events.h>
11 #include <Model_Session.h>
12 #include <Model_ResultPart.h>
13 #include <Model_ResultConstruction.h>
14 #include <Model_ResultBody.h>
15 #include <Model_ResultCompSolid.h>
16 #include <Model_ResultGroup.h>
17 #include <Model_ResultParameter.h>
18 #include <ModelAPI_Validator.h>
19 #include <ModelAPI_CompositeFeature.h>
20 #include <ModelAPI_Tools.h>
21
22 #include <Events_Loop.h>
23 #include <Events_Error.h>
24
25 #include <TDataStd_Integer.hxx>
26 #include <TDataStd_Comment.hxx>
27 #include <TDF_ChildIDIterator.hxx>
28 #include <TDataStd_ReferenceArray.hxx>
29 #include <TDataStd_HLabelArray1.hxx>
30 #include <TDataStd_Name.hxx>
31 #include <TDF_Reference.hxx>
32 #include <TDF_ChildIDIterator.hxx>
33 #include <TDF_LabelMapHasher.hxx>
34 #include <TDF_LabelMap.hxx>
35 #include <TDF_ListIteratorOfLabelList.hxx>
36
37 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
38
39 // feature sub-labels
40 static const int TAG_FEATURE_ARGUMENTS = 1;  ///< where the arguments are located
41 static const int TAG_FEATURE_RESULTS = 2;  ///< where the results are located
42
43 ///
44 /// 0:1:2 - where features are located
45 /// 0:1:2:N:1 - data of the feature N
46 /// 0:1:2:N:2:K:1 - data of the K result of the feature N
47
48 Model_Objects::Model_Objects(TDF_Label theMainLab) : myMain(theMainLab)
49 {
50 }
51
52 void Model_Objects::setOwner(DocumentPtr theDoc)
53 {
54   myDoc = theDoc;
55   // update all fields and recreate features and result objects if needed
56   TDF_LabelList aNoUpdated;
57   synchronizeFeatures(aNoUpdated, true, true);
58   myHistory.clear();
59 }
60
61 Model_Objects::~Model_Objects()
62 {
63   // delete all features of this document
64   Events_Loop* aLoop = Events_Loop::loop();
65   // erase one by one to avoid access from the feature destructor itself from he map
66   while(!myFeatures.IsEmpty()) {
67     NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeaturesIter(myFeatures);
68     FeaturePtr aFeature = aFeaturesIter.Value();
69     static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
70     ModelAPI_EventCreator::get()->sendDeleted(myDoc, ModelAPI_Feature::group());
71     ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
72     aFeature->eraseResults();
73     aFeature->erase();
74     myFeatures.UnBind(aFeaturesIter.Key());
75   }
76   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
77   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
78
79 }
80
81 /// Appends to the array of references a new referenced label
82 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced, TDF_Label& thePrevLab)
83 {
84   Handle(TDataStd_ReferenceArray) aRefs;
85   if (!theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
86     aRefs = TDataStd_ReferenceArray::Set(theArrayLab, 0, 0);
87     aRefs->SetValue(0, theReferenced);
88   } else {  // extend array by one more element
89     Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
90                                                                         aRefs->Upper() + 1);
91     int aPassedPrev = 0; // prev feature is found and passed
92     if (thePrevLab.IsNull()) { // null means that inserted feature must be the first
93       aNewArray->SetValue(aRefs->Lower(), theReferenced);
94       aPassedPrev = 1;
95     }
96     for (int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
97       aNewArray->SetValue(a + aPassedPrev, aRefs->Value(a));
98       if (!aPassedPrev && aRefs->Value(a).IsEqual(thePrevLab)) {
99         aPassedPrev = 1;
100         aNewArray->SetValue(a + 1, theReferenced);
101       }
102     }
103     if (!aPassedPrev) // not found: unknown situation
104       aNewArray->SetValue(aRefs->Upper() + 1, theReferenced);
105     aRefs->SetInternalArray(aNewArray);
106   }
107 }
108
109 void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterThis)
110 {
111   if (!theFeature->isAction()) {  // do not add action to the data model
112     TDF_Label aFeaturesLab = featuresLabel();
113     TDF_Label aFeatureLab = aFeaturesLab.NewChild();
114     // store feature in the features array: before "initData" because in macro features
115     // in initData it creates new features, appeared later than this
116     TDF_Label aPrevFeateureLab;
117     if (theAfterThis.get()) { // searching for the previous feature label
118       std::shared_ptr<Model_Data> aPrevData = 
119         std::dynamic_pointer_cast<Model_Data>(theAfterThis->data());
120       if (aPrevData.get()) {
121         aPrevFeateureLab = aPrevData->label().Father();
122       }
123     }
124     AddToRefArray(aFeaturesLab, aFeatureLab, aPrevFeateureLab);
125
126     // keep the feature ID to restore document later correctly
127     TDataStd_Comment::Set(aFeatureLab, theFeature->getKind().c_str());
128     myFeatures.Bind(aFeatureLab, theFeature);
129     // must be after binding to the map because of "Box" macro feature that 
130     // creates other features in "initData"
131     initData(theFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS);
132     // event: feature is added
133     static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
134     ModelAPI_EventCreator::get()->sendUpdated(theFeature, anEvent);
135     theFeature->setDisabled(false); // by default created feature is enabled
136     updateHistory(ModelAPI_Feature::group());
137   } else { // make feature has not-null data anyway
138     theFeature->setData(Model_Data::invalidData());
139     theFeature->setDoc(myDoc);
140   }
141 }
142
143 /// Appends to the array of references a new referenced label.
144 /// If theIndex is not -1, removes element at this index, not theReferenced.
145 /// \returns the index of removed element
146 static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, 
147   const int theIndex = -1)
148 {
149   int aResult = -1;  // no returned
150   Handle(TDataStd_ReferenceArray) aRefs;
151   if (theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
152     if (aRefs->Length() == 1) {  // just erase an array
153       if ((theIndex == -1 && aRefs->Value(0) == theReferenced) || theIndex == 0) {
154         theArrayLab.ForgetAttribute(TDataStd_ReferenceArray::GetID());
155       }
156       aResult = 0;
157     } else {  // reduce the array
158       Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
159                                                                           aRefs->Upper() - 1);
160       int aCount = aRefs->Lower();
161       for (int a = aCount; a <= aRefs->Upper(); a++, aCount++) {
162         if ((theIndex == -1 && aRefs->Value(a) == theReferenced) || theIndex == a) {
163           aCount--;
164           aResult = a;
165         } else {
166           aNewArray->SetValue(aCount, aRefs->Value(a));
167         }
168       }
169       aRefs->SetInternalArray(aNewArray);
170     }
171   }
172   return aResult;
173 }
174
175 void Model_Objects::refsToFeature(FeaturePtr theFeature,
176   std::set<std::shared_ptr<ModelAPI_Feature> >& theRefs, const bool isSendError)
177 {
178   // check the feature: it must have no depended objects on it
179   // the dependencies can be in the feature results
180   std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
181   for(; aResIter != theFeature->results().cend(); aResIter++) {
182     ResultPtr aResult = (*aResIter);
183     std::shared_ptr<Model_Data> aData = 
184       std::dynamic_pointer_cast<Model_Data>(aResult->data());
185     if (aData.get() != NULL) {
186       const std::set<AttributePtr>& aRefs = aData->refsToMe();
187       std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end();
188       for(; aRefIt != aRefLast; aRefIt++) {
189         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIt)->owner());
190         if (aFeature.get() != NULL)
191           theRefs.insert(aFeature);
192       }
193     }
194   }
195   // the dependencies can be in the feature itself
196   std::shared_ptr<Model_Data> aData = 
197       std::dynamic_pointer_cast<Model_Data>(theFeature->data());
198   if (aData && !aData->refsToMe().empty()) {
199     const std::set<AttributePtr>& aRefs = aData->refsToMe();
200     std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end();
201     for(; aRefIt != aRefLast; aRefIt++) {
202       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIt)->owner());
203       if (aFeature.get() != NULL)
204         theRefs.insert(aFeature);
205     }
206   }
207
208   if (!theRefs.empty() && isSendError) {
209     Events_Error::send(
210       "Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
211   }
212 }
213
214 void Model_Objects::removeFeature(FeaturePtr theFeature)
215 {
216   std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theFeature->data());
217   if (aData && aData->isValid()) {
218     // checking that the sub-element of composite feature is removed: if yes, inform the owner
219     std::set<std::shared_ptr<ModelAPI_Feature> > aRefs;
220     refsToFeature(theFeature, aRefs, false);
221     std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aRefIter = aRefs.begin();
222     for(; aRefIter != aRefs.end(); aRefIter++) {
223       std::shared_ptr<ModelAPI_CompositeFeature> aComposite = 
224         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aRefIter);
225       if (aComposite.get()) {
226         aComposite->removeFeature(theFeature);
227       }
228     }
229     // this must be before erase since theFeature erasing removes all information about
230     // the feature results and groups of results
231     // To reproduce: create sketch, extrusion, remove sketch => constructions tree is not updated
232     clearHistory(theFeature);
233     // erase fields
234     theFeature->erase();
235
236     TDF_Label aFeatureLabel = aData->label().Father();
237     if (myFeatures.IsBound(aFeatureLabel))
238       myFeatures.UnBind(aFeatureLabel);
239
240     static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
241     ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
242     // erase all attributes under the label of feature
243     aFeatureLabel.ForgetAllAttributes();
244     // remove it from the references array
245     RemoveFromRefArray(featuresLabel(), aFeatureLabel);
246     // event: feature is deleted
247     ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
248     // the redisplay signal should be flushed in order to erase the feature presentation in the viewer
249     Events_Loop::loop()->flush(EVENT_DISP);
250     updateHistory(ModelAPI_Feature::group());
251   }
252 }
253
254 void Model_Objects::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
255 {
256   TDF_Label aFeaturesLab = featuresLabel();
257   Handle(TDataStd_ReferenceArray) aRefs;
258   if (!aFeaturesLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
259     return;
260   TDF_Label anAfterLab, aMovedLab = 
261     std::dynamic_pointer_cast<Model_Data>(theMoved->data())->label().Father();
262   if (theAfterThis.get())
263     anAfterLab = std::dynamic_pointer_cast<Model_Data>(theAfterThis->data())->label().Father();
264
265   Handle(TDataStd_HLabelArray1) aNewArray = 
266     new TDataStd_HLabelArray1(aRefs->Lower(), aRefs->Upper());
267   int aPassedMovedFrom = 0; // the prev feature location is found and passed
268   int aPassedMovedTo = 0; // the feature is added and this location is passed
269   if (!theAfterThis.get()) { // null means that inserted feature must be the first
270     aNewArray->SetValue(aRefs->Lower(), aMovedLab);
271     aPassedMovedTo = 1;
272   }
273   for (int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
274     if (aPassedMovedTo == 0 && aRefs->Value(a) == anAfterLab) { // add two
275       aPassedMovedTo++;
276       aNewArray->SetValue(a - aPassedMovedFrom, anAfterLab);
277       if (a + 1 - aPassedMovedFrom <= aRefs->Upper())
278         aNewArray->SetValue(a + 1 - aPassedMovedFrom, aMovedLab);
279     } else if (aPassedMovedFrom == 0 && aRefs->Value(a) == aMovedLab) { // skip
280       aPassedMovedFrom++;
281     } else { // just copy one
282       if (a - aPassedMovedFrom + aPassedMovedTo <= aRefs->Upper())
283         aNewArray->SetValue(a - aPassedMovedFrom + aPassedMovedTo, aRefs->Value(a));
284     }
285   }
286   if (!aPassedMovedFrom || !aPassedMovedTo) {// not found: unknown situation
287     if (!aPassedMovedFrom) {
288       static std::string aMovedFromError("The moved feature is not found");
289       Events_Error::send(aMovedFromError);
290     } else {
291       static std::string aMovedToError("The 'after' feature for movement is not found");
292       Events_Error::send(aMovedToError);
293     }
294     return;
295   }
296   // store the new array
297   aRefs->SetInternalArray(aNewArray);
298   // update the feature and the history
299   clearHistory(theMoved);
300   // make sure all (selection) attributes of moved feature will be updated
301   theMoved->data()->setUpdateID(0);
302   static Events_ID EVENT_UPD = Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED);
303   ModelAPI_EventCreator::get()->sendUpdated(theMoved, EVENT_UPD);
304   ModelAPI_EventCreator::get()->sendReordered(theMoved->document(), theMoved->groupName());
305 }
306
307 void Model_Objects::clearHistory(ObjectPtr theObj)
308 {
309   if (theObj) {
310     const std::string aGroup = theObj->groupName();
311     std::map<std::string, std::vector<ObjectPtr> >::iterator aHIter = myHistory.find(aGroup);
312     if (aHIter != myHistory.end())
313       myHistory.erase(aHIter); // erase from map => this means that it is not synchronized
314     if (theObj->groupName() == ModelAPI_Feature::group()) { // clear results group of the feature
315       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
316       std::string aResultGroup = featureResultGroup(aFeature);
317       if (!aResultGroup.empty()) {
318         std::map<std::string, std::vector<ObjectPtr> >::iterator aHIter = 
319           myHistory.find(aResultGroup);
320         if (aHIter != myHistory.end())
321           myHistory.erase(aHIter); // erase from map => this means that it is not synchronized
322       }
323     }
324   }
325 }
326
327 void Model_Objects::createHistory(const std::string& theGroupID)
328 {
329   std::map<std::string, std::vector<ObjectPtr> >::iterator aHIter = myHistory.find(theGroupID);
330   if (aHIter == myHistory.end()) {
331     myHistory[theGroupID] = std::vector<ObjectPtr>();
332     std::vector<ObjectPtr>& aResult = myHistory[theGroupID];
333     // iterate the array of references and get feature by feature from the array
334     bool isFeature = theGroupID == ModelAPI_Feature::group();
335     Handle(TDataStd_ReferenceArray) aRefs;
336     if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
337       for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
338         FeaturePtr aFeature = feature(aRefs->Value(a));
339         if (aFeature.get()) {
340           // if feature is in sub-component, remove it from history: it is in sub-tree of sub-component
341           if (!ModelAPI_Tools::compositeOwner(aFeature).get()) {
342             if (isFeature) { // here may be also disabled features
343               if (aFeature->isInHistory()) {
344                 aResult.push_back(aFeature);
345               }
346             } else if (!aFeature->isDisabled()) { // iterate all results of not-disabled feature
347               const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
348               std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
349               for (; aRIter != aResults.cend(); aRIter++) {
350                 ResultPtr aRes = *aRIter;
351                 if (aRes->groupName() != theGroupID) break; // feature have only same group results
352                 if (!aRes->isDisabled() && aRes->isInHistory() && !aRes->isConcealed()) {
353                   aResult.push_back(*aRIter);
354                 }
355               }
356             }
357           }
358         }
359       }
360     }
361   }
362 }
363
364 void Model_Objects::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
365 {
366   clearHistory(theObject);
367 }
368
369 void Model_Objects::updateHistory(const std::string theGroup)
370 {
371   std::map<std::string, std::vector<ObjectPtr> >::iterator aHIter = myHistory.find(theGroup);
372   if (aHIter != myHistory.end())
373     myHistory.erase(aHIter); // erase from map => this means that it is not synchronized
374 }
375
376 FeaturePtr Model_Objects::feature(TDF_Label theLabel) const
377 {
378   if (myFeatures.IsBound(theLabel))
379     return myFeatures.Find(theLabel);
380   return FeaturePtr();  // not found
381 }
382
383 ObjectPtr Model_Objects::object(TDF_Label theLabel)
384 {
385   // try feature by label
386   FeaturePtr aFeature = feature(theLabel);
387   if (aFeature)
388     return feature(theLabel);
389   TDF_Label aFeatureLabel = theLabel.Father().Father();  // let's suppose it is result
390   aFeature = feature(aFeatureLabel);
391   bool isSubResult = false;
392   if (!aFeature.get() && aFeatureLabel.Depth() > 1) { // let's suppose this is sub-result of result
393     aFeatureLabel = aFeatureLabel.Father().Father();
394     aFeature = feature(aFeatureLabel);
395     isSubResult = true;
396   }
397   if (aFeature) {
398     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
399     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.cbegin();
400     for (; aRIter != aResults.cend(); aRIter++) {
401       if (isSubResult) {
402         ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*aRIter);
403         if (aCompRes) {
404           int aNumSubs = aCompRes->numberOfSubs();
405           for(int a = 0; a < aNumSubs; a++) {
406             ResultPtr aSub = aCompRes->subResult(a);
407             if (aSub.get()) {
408               std::shared_ptr<Model_Data> aSubData = std::dynamic_pointer_cast<Model_Data>(
409                   aSub->data());
410               if (aSubData->label().Father().IsEqual(theLabel))
411                 return aSub;
412             }
413           }
414         }
415       } else {
416         std::shared_ptr<Model_Data> aResData = std::dynamic_pointer_cast<Model_Data>(
417             (*aRIter)->data());
418         if (aResData->label().Father().IsEqual(theLabel))
419           return *aRIter;
420       }
421     }
422   }
423   return FeaturePtr();  // not found
424 }
425
426 ObjectPtr Model_Objects::object(const std::string& theGroupID, const int theIndex)
427 {
428   if (theIndex == -1)
429     return ObjectPtr();
430   createHistory(theGroupID);
431   return myHistory[theGroupID][theIndex];
432 }
433
434 std::shared_ptr<ModelAPI_Object> Model_Objects::objectByName(
435     const std::string& theGroupID, const std::string& theName)
436 {
437   createHistory(theGroupID);
438   std::vector<ObjectPtr>& allObjs = myHistory[theGroupID];
439   std::vector<ObjectPtr>::iterator anObjIter = allObjs.begin();
440   for(; anObjIter != allObjs.end(); anObjIter++) {
441     if ((*anObjIter)->data()->name() == theName)
442       return *anObjIter;
443   }
444   // not found
445   return ObjectPtr();
446 }
447
448 const int Model_Objects::index(std::shared_ptr<ModelAPI_Object> theObject)
449 {
450   std::string aGroup = theObject->groupName();
451   createHistory(aGroup);
452   std::vector<ObjectPtr>& allObjs = myHistory[aGroup];
453   std::vector<ObjectPtr>::iterator anObjIter = allObjs.begin(); // iterate to search object
454   for(int anIndex = 0; anObjIter != allObjs.end(); anObjIter++, anIndex++) {
455     if ((*anObjIter) == theObject)
456       return anIndex;
457   }
458   // not found
459   return -1;
460 }
461
462 int Model_Objects::size(const std::string& theGroupID)
463 {
464   createHistory(theGroupID);
465   return myHistory[theGroupID].size();
466 }
467
468 void Model_Objects::allResults(const std::string& theGroupID, std::list<ResultPtr>& theResults)
469 {
470   // iterate the array of references and get feature by feature from the array
471   Handle(TDataStd_ReferenceArray) aRefs;
472   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
473     for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
474       FeaturePtr aFeature = feature(aRefs->Value(a));
475       if (aFeature.get()) {
476         const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
477         std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
478         for (; aRIter != aResults.cend(); aRIter++) {
479           ResultPtr aRes = *aRIter;
480           if (aRes->groupName() != theGroupID) break; // feature have only same group results
481           // iterate also concealed: ALL RESULTS (for translation parts undo/redo management)
482           //if (aRes->isInHistory() && !aRes->isConcealed()) {
483             theResults.push_back(*aRIter);
484           //}
485         }
486       }
487     }
488   }
489 }
490
491
492 TDF_Label Model_Objects::featuresLabel() const
493 {
494   return myMain.FindChild(TAG_OBJECTS);
495 }
496
497 void Model_Objects::setUniqueName(FeaturePtr theFeature)
498 {
499   if (!theFeature->data()->name().empty())
500     return;  // not needed, name is already defined
501   std::string aName;  // result
502   // first count all features of such kind to start with index = count + 1
503   int aNumObjects = -1; // this feature is already in this map
504   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myFeatures);
505   for (; aFIter.More(); aFIter.Next()) {
506     if (aFIter.Value()->getKind() == theFeature->getKind())
507       aNumObjects++;
508   }
509   // generate candidate name
510   std::stringstream aNameStream;
511   aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
512   aName = aNameStream.str();
513   // check this is unique, if not, increase index by 1
514   for (aFIter.Initialize(myFeatures); aFIter.More();) {
515     FeaturePtr aFeature = aFIter.Value();
516     bool isSameName = aFeature->data()->name() == aName;
517     if (!isSameName) {  // check also results to avoid same results names (actual for Parts)
518       const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
519       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
520       for (; aRIter != aResults.cend(); aRIter++) {
521         isSameName = (*aRIter)->data()->name() == aName;
522       }
523     }
524     if (isSameName) {
525       aNumObjects++;
526       std::stringstream aNameStream;
527       aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
528       aName = aNameStream.str();
529       // reinitialize iterator to make sure a new name is unique
530       aFIter.Initialize(myFeatures);
531     } else
532       aFIter.Next();
533   }
534   theFeature->data()->setName(aName);
535 }
536
537 void Model_Objects::initData(ObjectPtr theObj, TDF_Label theLab, const int theTag)
538 {
539   std::shared_ptr<Model_Data> aData(new Model_Data);
540   aData->setLabel(theLab.FindChild(theTag));
541   aData->setObject(theObj);
542   theObj->setDoc(myDoc);
543   theObj->setData(aData);
544   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
545   if (aFeature) {
546     setUniqueName(aFeature);  // must be before "initAttributes" because duplicate part uses name
547   }
548   theObj->initAttributes();
549 }
550
551 void Model_Objects::synchronizeFeatures(
552   const TDF_LabelList& theUpdated, const bool theUpdateReferences, const bool theFlush)
553 {
554   Model_Document* anOwner = std::dynamic_pointer_cast<Model_Document>(myDoc).get();
555   if (!anOwner) // this may happen on creation of document: nothing there, so nothing to synchronize
556     return;
557   // after all updates, sends a message that groups of features were created or updated
558   Events_Loop* aLoop = Events_Loop::loop();
559   static Events_ID aDispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
560   static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
561   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
562   static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
563   static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
564   static Events_ID aToHideEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
565   bool isActive = aLoop->activateFlushes(false);
566
567   // collect all updated labels map
568   TDF_LabelMap anUpdatedMap;
569   TDF_ListIteratorOfLabelList anUpdatedIter(theUpdated);
570   for(; anUpdatedIter.More(); anUpdatedIter.Next()) {
571     TDF_Label& aFeatureLab = anUpdatedIter.Value();
572     while(aFeatureLab.Depth() > 3)
573       aFeatureLab = aFeatureLab.Father();
574     if (myFeatures.IsBound(aFeatureLab))
575       anUpdatedMap.Add(aFeatureLab);
576   }
577
578   // update all objects by checking are they on labels or not
579   std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
580   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
581   for (; aLabIter.More(); aLabIter.Next()) {
582     TDF_Label aFeatureLabel = aLabIter.Value()->Label();
583     FeaturePtr aFeature;
584     if (!myFeatures.IsBound(aFeatureLabel)) {  // a new feature is inserted
585       // create a feature
586       aFeature = std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get())->createFeature(
587         TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
588         .ToCString(), anOwner);
589       if (!aFeature) {  // somethig is wrong, most probably, the opened document has invalid structure
590         Events_Error::send("Invalid type of object in the document");
591         aLabIter.Value()->Label().ForgetAllAttributes();
592         continue;
593       }
594       // this must be before "setData" to redo the sketch line correctly
595       myFeatures.Bind(aFeatureLabel, aFeature);
596       aNewFeatures.insert(aFeature);
597       initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS);
598       updateHistory(aFeature);
599       aFeature->setDisabled(false); // by default created feature is enabled (this allows to recreate the results before "setCurrent" is called)
600
601       // event: model is updated
602       ModelAPI_EventCreator::get()->sendUpdated(aFeature, aCreateEvent);
603     } else {  // nothing is changed, both iterators are incremented
604       aFeature = myFeatures.Find(aFeatureLabel);
605       aKeptFeatures.insert(aFeature);
606       if (anUpdatedMap.Contains(aFeatureLabel)) {
607         ModelAPI_EventCreator::get()->sendUpdated(aFeature, anUpdateEvent);
608       }
609     }
610   }
611
612   // check all features are checked: if not => it was removed
613   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myFeatures);
614   while (aFIter.More()) {
615     if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end()
616       && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
617         FeaturePtr aFeature = aFIter.Value();
618         // event: model is updated
619         //if (aFeature->isInHistory()) {
620         ModelAPI_EventCreator::get()->sendDeleted(myDoc, ModelAPI_Feature::group());
621         //}
622         // results of this feature must be redisplayed (hided)
623         // redisplay also removed feature (used for sketch and AISObject)
624         ModelAPI_EventCreator::get()->sendUpdated(aFeature, aRedispEvent);
625         updateHistory(aFeature);
626         aFeature->erase();
627         // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter
628         myFeatures.UnBind(aFIter.Key());
629         // reinitialize iterator because unbind may corrupt the previous order in the map
630         aFIter.Initialize(myFeatures);
631     } else
632       aFIter.Next();
633   }
634
635   if (theUpdateReferences) {
636     synchronizeBackRefs();
637   }
638   // update results of the features (after features created because they may be connected, like sketch and sub elements)
639   // After synchronisation of back references because sketch must be set in sub-elements before "execute" by updateResults
640   std::list<FeaturePtr> aComposites; // composites must be updated after their subs (issue 360)
641   TDF_ChildIDIterator aLabIter2(featuresLabel(), TDataStd_Comment::GetID());
642   for (; aLabIter2.More(); aLabIter2.Next()) {
643     TDF_Label aFeatureLabel = aLabIter2.Value()->Label();
644     if (myFeatures.IsBound(aFeatureLabel)) {  // a new feature is inserted
645       FeaturePtr aFeature = myFeatures.Find(aFeatureLabel);
646       if (std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature).get())
647         aComposites.push_back(aFeature);
648       updateResults(aFeature);
649     }
650   }
651   std::list<FeaturePtr>::iterator aComposite = aComposites.begin();
652   for(; aComposite != aComposites.end(); aComposite++) {
653     updateResults(*aComposite);
654   }
655
656   // the synchronize should be done after updateResults in order to correct back references of updated results
657   if (theUpdateReferences) {
658     synchronizeBackRefs();
659   }
660   if (!theUpdated.IsEmpty()) { // this means there is no control what was modified => remove history cash
661     myHistory.clear();
662   }
663
664   anOwner->executeFeatures() = false;
665   aLoop->activateFlushes(isActive);
666
667   if (theFlush) {
668     aLoop->flush(aCreateEvent);
669     aLoop->flush(aDeleteEvent);
670     aLoop->flush(anUpdateEvent);
671     aLoop->flush(aCreateEvent); // after update of features, there could be results created
672     aLoop->flush(aDeleteEvent); // or deleted
673     aLoop->flush(aRedispEvent);
674     aLoop->flush(aToHideEvent);
675   }
676   anOwner->executeFeatures() = true;
677 }
678
679 void Model_Objects::synchronizeBackRefs()
680 {
681   // keeps the concealed flags of result to catch the change and create created/deleted events
682   std::list<std::pair<ResultPtr, bool> > aConcealed;
683   // first cycle: erase all data about back-references
684   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeatures(myFeatures);
685   for(; aFeatures.More(); aFeatures.Next()) {
686     FeaturePtr aFeature = aFeatures.Value();
687     std::shared_ptr<Model_Data> aFData = 
688       std::dynamic_pointer_cast<Model_Data>(aFeature->data());
689     if (aFData) {
690       aFData->eraseBackReferences();
691     }
692     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
693     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
694     for (; aRIter != aResults.cend(); aRIter++) {
695       std::shared_ptr<Model_Data> aResData = 
696         std::dynamic_pointer_cast<Model_Data>((*aRIter)->data());
697       if (aResData.get()) {
698         aConcealed.push_back(std::pair<ResultPtr, bool>(*aRIter, (*aRIter)->isConcealed()));
699         aResData->eraseBackReferences();
700       }
701       // iterate sub-bodies of compsolid
702       ResultCompSolidPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*aRIter);
703       if (aComp.get()) {
704         int aNumSub = aComp->numberOfSubs();
705         for(int a = 0; a < aNumSub; a++) {
706           ResultPtr aSub = aComp->subResult(a);
707           std::shared_ptr<Model_Data> aResData = 
708             std::dynamic_pointer_cast<Model_Data>(aSub->data());
709           if (aResData.get()) {
710             aConcealed.push_back(std::pair<ResultPtr, bool>(aSub, aSub->isConcealed()));
711             aResData->eraseBackReferences();
712           }
713         }
714       }
715     }
716   }
717
718   // second cycle: set new back-references: only features may have reference, iterate only them
719   ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
720   for(aFeatures.Initialize(myFeatures); aFeatures.More(); aFeatures.Next()) {
721     FeaturePtr aFeature = aFeatures.Value();
722     std::shared_ptr<Model_Data> aFData = 
723       std::dynamic_pointer_cast<Model_Data>(aFeature->data());
724     if (aFData) {
725       std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
726       aFData->referencesToObjects(aRefs);
727       std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator 
728         aRefsIter = aRefs.begin();
729       for(; aRefsIter != aRefs.end(); aRefsIter++) {
730         std::list<ObjectPtr>::iterator aRefTo = aRefsIter->second.begin();
731         for(; aRefTo != aRefsIter->second.end(); aRefTo++) {
732           if (*aRefTo) {
733             std::shared_ptr<Model_Data> aRefData = 
734               std::dynamic_pointer_cast<Model_Data>((*aRefTo)->data());
735             aRefData->addBackReference(aFeature, aRefsIter->first); // here the Concealed flag is updated
736             // update enable/disable status: the nested status must be equal to the composite
737             CompositeFeaturePtr aComp = 
738               std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
739             if (aComp.get()) {
740               FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>(*aRefTo);
741               if (aReferenced.get()) {
742                 aReferenced->setDisabled(aComp->isDisabled());
743               }
744             }
745           }
746         }
747       }
748     }
749   }
750   std::list<std::pair<ResultPtr, bool> >::iterator aCIter = aConcealed.begin();
751   for(; aCIter != aConcealed.end(); aCIter++) {
752     if (aCIter->first->isConcealed() != aCIter->second) { // somethign is changed => produce event
753       if (aCIter->second) { // was concealed become not => creation event
754         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
755         ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, anEvent);
756       } else { // was not concealed become concealed => delete event
757         ModelAPI_EventCreator::get()->sendDeleted(myDoc, aCIter->first->groupName());
758         // redisplay for the viewer (it must be disappeared also)
759         static Events_ID EVENT_DISP = 
760           Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
761         ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, EVENT_DISP);
762       }
763     }
764   }
765 }
766
767 TDF_Label Model_Objects::resultLabel(
768   const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theResultIndex) 
769 {
770   const std::shared_ptr<Model_Data>& aData = 
771     std::dynamic_pointer_cast<Model_Data>(theFeatureData);
772   return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1);
773 }
774
775 void Model_Objects::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
776                                  std::shared_ptr<ModelAPI_Result> theResult,
777                                  const int theResultIndex)
778 {
779   theResult->setDoc(myDoc);
780   initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS);
781   if (theResult->data()->name().empty()) {  // if was not initialized, generate event and set a name
782     std::stringstream aNewName;
783     aNewName<<theFeatureData->name();
784     // if there are several results (issue #899: any number of result), add unique prefix starting from second
785     if (theResultIndex > 0 || theResult->groupName() == ModelAPI_ResultBody::group())
786       aNewName<<"_"<<theResultIndex + 1;
787     theResult->data()->setName(aNewName.str());
788   }
789 }
790
791 std::shared_ptr<ModelAPI_ResultConstruction> Model_Objects::createConstruction(
792     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
793 {
794   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
795   TDataStd_Comment::Set(aLab, ModelAPI_ResultConstruction::group().c_str());
796   ObjectPtr anOldObject = object(aLab);
797   std::shared_ptr<ModelAPI_ResultConstruction> aResult;
798   if (anOldObject) {
799     aResult = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(anOldObject);
800   }
801   if (!aResult) {
802     aResult = std::shared_ptr<ModelAPI_ResultConstruction>(new Model_ResultConstruction);
803     storeResult(theFeatureData, aResult, theIndex);
804   }
805   return aResult;
806 }
807
808 std::shared_ptr<ModelAPI_ResultBody> Model_Objects::createBody(
809     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
810 {
811   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
812   // for feature create compsolid, but for result sub create body: 
813   // only one level of recursion is supported now
814   ResultPtr aResultOwner = std::dynamic_pointer_cast<ModelAPI_Result>(theFeatureData->owner());
815   ObjectPtr anOldObject;
816   if (aResultOwner.get()) {
817     TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str());
818   } else { // in compsolid (higher level result) old object probably may be found
819     TDataStd_Comment::Set(aLab, ModelAPI_ResultCompSolid::group().c_str());
820     anOldObject = object(aLab);
821   }
822   std::shared_ptr<ModelAPI_ResultBody> aResult;
823   if (anOldObject) {
824     aResult = std::dynamic_pointer_cast<ModelAPI_ResultBody>(anOldObject);
825   }
826   if (!aResult) {
827     // create compsolid anyway; if it is compsolid, it will create sub-bodies internally
828     if (aResultOwner.get()) {
829       aResult = std::shared_ptr<ModelAPI_ResultBody>(new Model_ResultBody);
830     } else {
831       aResult = std::shared_ptr<ModelAPI_ResultBody>(new Model_ResultCompSolid);
832     }
833     storeResult(theFeatureData, aResult, theIndex);
834   }
835   return aResult;
836 }
837
838 std::shared_ptr<ModelAPI_ResultPart> Model_Objects::createPart(
839     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
840 {
841   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
842   TDataStd_Comment::Set(aLab, ModelAPI_ResultPart::group().c_str());
843   ObjectPtr anOldObject = object(aLab);
844   std::shared_ptr<ModelAPI_ResultPart> aResult;
845   if (anOldObject) {
846     aResult = std::dynamic_pointer_cast<ModelAPI_ResultPart>(anOldObject);
847   }
848   if (!aResult) {
849     aResult = std::shared_ptr<ModelAPI_ResultPart>(new Model_ResultPart);
850     storeResult(theFeatureData, aResult, theIndex);
851   }
852   return aResult;
853 }
854
855 std::shared_ptr<ModelAPI_ResultPart> Model_Objects::copyPart(
856     const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
857     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
858 {
859   std::shared_ptr<ModelAPI_ResultPart> aResult = createPart(theFeatureData, theIndex);
860   aResult->data()->reference(Model_ResultPart::BASE_REF_ID())->setValue(theOrigin);
861   return aResult;
862 }
863
864 std::shared_ptr<ModelAPI_ResultGroup> Model_Objects::createGroup(
865     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
866 {
867   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
868   TDataStd_Comment::Set(aLab, ModelAPI_ResultGroup::group().c_str());
869   ObjectPtr anOldObject = object(aLab);
870   std::shared_ptr<ModelAPI_ResultGroup> aResult;
871   if (anOldObject) {
872     aResult = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anOldObject);
873   }
874   if (!aResult) {
875     aResult = std::shared_ptr<ModelAPI_ResultGroup>(new Model_ResultGroup(theFeatureData));
876     storeResult(theFeatureData, aResult, theIndex);
877   }
878   return aResult;
879 }
880
881 std::shared_ptr<ModelAPI_ResultParameter> Model_Objects::createParameter(
882       const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
883 {
884   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
885   TDataStd_Comment::Set(aLab, ModelAPI_ResultParameter::group().c_str());
886   ObjectPtr anOldObject = object(aLab);
887   std::shared_ptr<ModelAPI_ResultParameter> aResult;
888   if (anOldObject) {
889     aResult = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(anOldObject);
890   }
891   if (!aResult) {
892     aResult = std::shared_ptr<ModelAPI_ResultParameter>(new Model_ResultParameter);
893     storeResult(theFeatureData, aResult, theIndex);
894   }
895   return aResult;
896 }
897
898 std::shared_ptr<ModelAPI_Feature> Model_Objects::feature(
899     const std::shared_ptr<ModelAPI_Result>& theResult)
900 {
901   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(theResult->data());
902   if (aData) {
903     TDF_Label aFeatureLab = aData->label().Father().Father().Father();
904     FeaturePtr aFeature = feature(aFeatureLab);
905     if (!aFeature.get() && aFeatureLab.Depth() > 1) { // this may be sub-result of result
906       aFeatureLab = aFeatureLab.Father().Father();
907       aFeature = feature(aFeatureLab);
908     }
909     return aFeature;
910   }
911   return FeaturePtr();
912 }
913
914 std::string Model_Objects::featureResultGroup(FeaturePtr theFeature)
915 {
916   if (theFeature->data()->isValid()) {
917     TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father());
918     if (aLabIter.More()) {
919       TDF_Label anArgLab = aLabIter.Value();
920       Handle(TDataStd_Comment) aGroup;
921       if (aLabIter.Value().FindAttribute(TDataStd_Comment::GetID(), aGroup)) {
922         return TCollection_AsciiString(aGroup->Get()).ToCString();
923       }
924     }
925   }
926   static std::string anEmpty;
927   return anEmpty; // not found
928 }
929
930 void Model_Objects::updateResults(FeaturePtr theFeature)
931 {
932   // for not persistent is will be done by parametric updater automatically
933   //if (!theFeature->isPersistentResult()) return;
934   // check the existing results and remove them if there is nothing on the label
935   std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
936   while(aResIter != theFeature->results().cend()) {
937     ResultPtr aBody = std::dynamic_pointer_cast<ModelAPI_Result>(*aResIter);
938     if (aBody.get()) {
939       std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(aBody->data());
940       if (!aData.get() || !aData->isValid() || (!aBody->isDisabled() && aData->isDeleted())) { 
941         // found a disappeared result => remove it
942         theFeature->eraseResultFromList(aBody);
943         // start iterate from beginning because iterator is corrupted by removing
944         aResIter = theFeature->results().cbegin();
945         continue;
946       }
947     }
948     aResIter++;
949   }
950   // it may be on undo
951   if (!theFeature->data() || !theFeature->data()->isValid() || theFeature->isDisabled())
952     return;
953   // check that results are presented on all labels
954   int aResSize = theFeature->results().size();
955   TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father());
956   for(; aLabIter.More(); aLabIter.Next()) {
957     // here must be GUID of the feature
958     int aResIndex = aLabIter.Value().Tag() - 1;
959     ResultPtr aNewBody;
960     if (aResSize <= aResIndex) {
961       TDF_Label anArgLab = aLabIter.Value();
962       Handle(TDataStd_Comment) aGroup;
963       if (anArgLab.FindAttribute(TDataStd_Comment::GetID(), aGroup)) {
964         if (aGroup->Get() == ModelAPI_ResultBody::group().c_str() || 
965             aGroup->Get() == ModelAPI_ResultCompSolid::group().c_str()) {
966           aNewBody = createBody(theFeature->data(), aResIndex);
967         } else if (aGroup->Get() == ModelAPI_ResultPart::group().c_str()) {
968           std::shared_ptr<ModelAPI_ResultPart> aNewP = createPart(theFeature->data(), aResIndex); 
969           theFeature->setResult(aNewP, aResIndex);
970           if (!aNewP->partDoc().get())
971             theFeature->execute(); // create the part result: it is better to restore the previous result if it is possible
972           break;
973         } else if (aGroup->Get() == ModelAPI_ResultConstruction::group().c_str()) {
974           theFeature->execute(); // construction shapes are needed for sketch solver
975           break;
976         } else if (aGroup->Get() == ModelAPI_ResultGroup::group().c_str()) {
977           aNewBody = createGroup(theFeature->data(), aResIndex);
978         } else if (aGroup->Get() == ModelAPI_ResultParameter::group().c_str()) {
979           theFeature->attributeChanged("expression"); // just produce a value
980           break;
981         } else {
982           Events_Error::send(std::string("Unknown type of result is found in the document:") +
983             TCollection_AsciiString(aGroup->Get()).ToCString());
984         }
985       }
986       if (aNewBody && !aNewBody->data()->isDeleted()) {
987         theFeature->setResult(aNewBody, aResIndex);
988       }
989     }
990   }
991 }
992
993 ResultPtr Model_Objects::findByName(const std::string theName)
994 {
995   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator anObjIter(myFeatures);
996   for(; anObjIter.More(); anObjIter.Next()) {
997     FeaturePtr& aFeature = anObjIter.ChangeValue();
998     if (!aFeature.get() || aFeature->isDisabled()) // may be on close
999       continue;
1000     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
1001     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
1002     for (; aRIter != aResults.cend(); aRIter++) {
1003       ResultPtr aRes = *aRIter;
1004       if (aRes.get() && aRes->data() && aRes->data()->isValid() && !aRes->isDisabled() &&
1005           aRes->data()->name() == theName) {
1006         return aRes;
1007       }
1008     }
1009   }
1010   // not found
1011   return ResultPtr();
1012 }
1013
1014 FeaturePtr Model_Objects::nextFeature(FeaturePtr theCurrent, const bool theReverse)
1015 {
1016   std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
1017   if (aData && aData->isValid()) {
1018     TDF_Label aFeatureLabel = aData->label().Father();
1019     Handle(TDataStd_ReferenceArray) aRefs;
1020     if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1021       for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { // iterate all existing features
1022         TDF_Label aCurLab = aRefs->Value(a);
1023         if (aCurLab.IsEqual(aFeatureLabel)) {
1024           a += theReverse ? -1 : 1;
1025           if (a >= aRefs->Lower() && a <= aRefs->Upper())
1026             return feature(aRefs->Value(a));
1027           break; // finish iiteration: it's last feature
1028         }
1029       }
1030     }
1031   }
1032   return FeaturePtr(); // not found, last, or something is wrong
1033 }
1034
1035 FeaturePtr Model_Objects::firstFeature()
1036 {
1037   Handle(TDataStd_ReferenceArray) aRefs;
1038   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1039     return feature(aRefs->Value(aRefs->Lower()));
1040   }
1041   return FeaturePtr(); // no features at all
1042 }
1043
1044 FeaturePtr Model_Objects::lastFeature()
1045 {
1046   Handle(TDataStd_ReferenceArray) aRefs;
1047   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1048     return feature(aRefs->Value(aRefs->Upper()));
1049   }
1050   return FeaturePtr(); // no features at all
1051 }
1052
1053 bool Model_Objects::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const
1054 {
1055   std::shared_ptr<Model_Data> aLaterD = std::static_pointer_cast<Model_Data>(theLater->data());
1056   std::shared_ptr<Model_Data> aCurrentD = std::static_pointer_cast<Model_Data>(theCurrent->data());
1057   if (aLaterD && aLaterD->isValid() && aCurrentD && aCurrentD->isValid()) {
1058     TDF_Label aLaterL = aLaterD->label().Father();
1059     TDF_Label aCurrentL = aCurrentD->label().Father();
1060     int aLaterI = -1, aCurentI = -1; // not found yet state
1061     Handle(TDataStd_ReferenceArray) aRefs;
1062     if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1063       for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { // iterate all existing features
1064         TDF_Label aCurLab = aRefs->Value(a);
1065         if (aCurLab.IsEqual(aLaterL)) {
1066           aLaterI = a;
1067         } else if (aCurLab.IsEqual(aCurrentL)) {
1068           aCurentI = a;
1069         } else continue;
1070         if (aLaterI != -1 && aCurentI != -1) // both are found
1071           return aLaterI > aCurentI;
1072       }
1073     }
1074   }
1075   return false; // not found, or something is wrong
1076 }
1077
1078 std::list<std::shared_ptr<ModelAPI_Feature> > Model_Objects::allFeatures()
1079 {
1080   std::list<std::shared_ptr<ModelAPI_Feature> > aResult;
1081   Handle(TDataStd_ReferenceArray) aRefs;
1082   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1083     for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
1084       FeaturePtr aFeature = feature(aRefs->Value(a));
1085       if (aFeature.get())
1086         aResult.push_back(aFeature);
1087     }
1088   }
1089   return aResult;
1090 }
1091
1092 int Model_Objects::numInternalFeatures()
1093 {
1094   Handle(TDataStd_ReferenceArray) aRefs;
1095   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1096     return aRefs->Upper() - aRefs->Lower() + 1;
1097   }
1098   return 0; // invalid
1099 }
1100
1101 std::shared_ptr<ModelAPI_Feature> Model_Objects::internalFeature(const int theIndex)
1102 {
1103   Handle(TDataStd_ReferenceArray) aRefs;
1104   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
1105     return feature(aRefs->Value(aRefs->Lower() + theIndex));
1106   }
1107   return FeaturePtr(); // invalid
1108 }
1109
1110 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
1111 {
1112   return TDF_LabelMapHasher::HashCode(theLab, theUpper);
1113
1114 }
1115 Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
1116 {
1117   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
1118 }