Salome HOME
3efd64be7ee7be75491a628bf3790a68150f4d59
[modules/shaper.git] / src / Model / Model_Document.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_Document.cxx
4 // Created:     28 Feb 2014
5 // Author:      Mikhail PONIKAROV
6
7 #include <Model_Document.h>
8 #include <Model_Data.h>
9 #include <Model_Application.h>
10 #include <Model_Session.h>
11 #include <Model_Events.h>
12 #include <Model_ResultPart.h>
13 #include <Model_ResultConstruction.h>
14 #include <Model_ResultBody.h>
15 #include <Model_ResultGroup.h>
16 #include <ModelAPI_Validator.h>
17 #include <ModelAPI_CompositeFeature.h>
18 #include <Events_Loop.h>
19 #include <Events_Error.h>
20
21 #include <TDataStd_Integer.hxx>
22 #include <TDataStd_Comment.hxx>
23 #include <TDF_ChildIDIterator.hxx>
24 #include <TDataStd_ReferenceArray.hxx>
25 #include <TDataStd_HLabelArray1.hxx>
26 #include <TDataStd_Name.hxx>
27 #include <TDF_Reference.hxx>
28 #include <TDF_ChildIDIterator.hxx>
29 #include <TDF_LabelMapHasher.hxx>
30 #include <OSD_File.hxx>
31 #include <OSD_Path.hxx>
32
33 #include <climits>
34 #ifndef WIN32
35 #include <sys/stat.h>
36 #endif
37
38 #ifdef WIN32
39 # define _separator_ '\\'
40 #else
41 # define _separator_ '/'
42 #endif
43
44 static const int UNDO_LIMIT = 1000;  // number of possible undo operations (big for sketcher)
45
46 static const int TAG_GENERAL = 1;  // general properties tag
47 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
48 static const int TAG_HISTORY = 3;  // tag of the history sub-tree (python dump)
49
50 // feature sub-labels
51 static const int TAG_FEATURE_ARGUMENTS = 1;  ///< where the arguments are located
52 static const int TAG_FEATURE_RESULTS = 2;  ///< where the results are located
53
54 ///
55 /// 0:1:2 - where features are located
56 /// 0:1:2:N:1 - data of the feature N
57 /// 0:1:2:N:2:K:1 - data of the K result of the feature N
58
59 Model_Document::Model_Document(const std::string theID, const std::string theKind)
60     : myID(theID), myKind(theKind),
61       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
62 {
63   myDoc->SetUndoLimit(UNDO_LIMIT);  
64   myTransactionSave = 0;
65   myExecuteFeatures = true;
66   // to have something in the document and avoid empty doc open/save problem
67   // in transaction for nesting correct working
68   myDoc->NewCommand();
69   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
70   myDoc->CommitCommand();
71 }
72
73 /// Returns the file name of this document by the nameof directory and identifuer of a document
74 static TCollection_ExtendedString DocFileName(const char* theFileName, const std::string& theID)
75 {
76   TCollection_ExtendedString aPath((const Standard_CString) theFileName);
77   // remove end-separators
78   while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
79     aPath.Remove(aPath.Length());
80   aPath += _separator_;
81   aPath += theID.c_str();
82   aPath += ".cbf";  // standard binary file extension
83   return aPath;
84 }
85
86 bool Model_Document::load(const char* theFileName)
87 {
88   Handle(Model_Application) anApp = Model_Application::getApplication();
89   if (this == Model_Session::get()->moduleDocument().get()) {
90     anApp->setLoadPath(theFileName);
91   }
92   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
93   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
94   try {
95     aStatus = anApp->Open(aPath, myDoc);
96   } catch (Standard_Failure) {
97     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
98     Events_Error::send(
99         std::string("Exception in opening of document: ") + aFail->GetMessageString());
100     return false;
101   }
102   bool isError = aStatus != PCDM_RS_OK;
103   if (isError) {
104     switch (aStatus) {
105       case PCDM_RS_UnknownDocument:
106         Events_Error::send(std::string("Can not open document"));
107         break;
108       case PCDM_RS_AlreadyRetrieved:
109         Events_Error::send(std::string("Can not open document: already opened"));
110         break;
111       case PCDM_RS_AlreadyRetrievedAndModified:
112         Events_Error::send(
113             std::string("Can not open document: already opened and modified"));
114         break;
115       case PCDM_RS_NoDriver:
116         Events_Error::send(std::string("Can not open document: driver library is not found"));
117         break;
118       case PCDM_RS_UnknownFileDriver:
119         Events_Error::send(std::string("Can not open document: unknown driver for opening"));
120         break;
121       case PCDM_RS_OpenError:
122         Events_Error::send(std::string("Can not open document: file open error"));
123         break;
124       case PCDM_RS_NoVersion:
125         Events_Error::send(std::string("Can not open document: invalid version"));
126         break;
127       case PCDM_RS_NoModel:
128         Events_Error::send(std::string("Can not open document: no data model"));
129         break;
130       case PCDM_RS_NoDocument:
131         Events_Error::send(std::string("Can not open document: no document inside"));
132         break;
133       case PCDM_RS_FormatFailure:
134         Events_Error::send(std::string("Can not open document: format failure"));
135         break;
136       case PCDM_RS_TypeNotFoundInSchema:
137         Events_Error::send(std::string("Can not open document: invalid object"));
138         break;
139       case PCDM_RS_UnrecognizedFileFormat:
140         Events_Error::send(std::string("Can not open document: unrecognized file format"));
141         break;
142       case PCDM_RS_MakeFailure:
143         Events_Error::send(std::string("Can not open document: make failure"));
144         break;
145       case PCDM_RS_PermissionDenied:
146         Events_Error::send(std::string("Can not open document: permission denied"));
147         break;
148       case PCDM_RS_DriverFailure:
149         Events_Error::send(std::string("Can not open document: driver failure"));
150         break;
151       default:
152         Events_Error::send(std::string("Can not open document: unknown error"));
153         break;
154     }
155   }
156   if (!isError) {
157     myDoc->SetUndoLimit(UNDO_LIMIT);
158     // to avoid the problem that feature is created in the current, not this, document
159     std::shared_ptr<Model_Session> aSession = 
160       std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
161     aSession->setActiveDocument(anApp->getDocument(myID), false);
162     aSession->setCheckTransactions(false);
163     synchronizeFeatures(false, true);
164     aSession->setCheckTransactions(true);
165     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
166     aSession->setActiveDocument(anApp->getDocument(myID), true);
167   }
168   return !isError;
169 }
170
171 bool Model_Document::save(const char* theFileName, std::list<std::string>& theResults)
172 {
173   // create a directory in the root document if it is not yet exist
174   Handle(Model_Application) anApp = Model_Application::getApplication();
175   if (this == Model_Session::get()->moduleDocument().get()) {
176 #ifdef WIN32
177     CreateDirectory(theFileName, NULL);
178 #else
179     mkdir(theFileName, 0x1ff);
180 #endif
181   }
182   // filename in the dir is id of document inside of the given directory
183   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
184   PCDM_StoreStatus aStatus;
185   try {
186     aStatus = anApp->SaveAs(myDoc, aPath);
187   } catch (Standard_Failure) {
188     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
189     Events_Error::send(
190         std::string("Exception in saving of document: ") + aFail->GetMessageString());
191     return false;
192   }
193   bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
194   if (!isDone) {
195     switch (aStatus) {
196       case PCDM_SS_DriverFailure:
197         Events_Error::send(std::string("Can not save document: save driver-library failure"));
198         break;
199       case PCDM_SS_WriteFailure:
200         Events_Error::send(std::string("Can not save document: file writing failure"));
201         break;
202       case PCDM_SS_Failure:
203       default:
204         Events_Error::send(std::string("Can not save document"));
205         break;
206     }
207   }
208   myTransactionSave = myTransactions.size();
209   if (isDone) {  // save also sub-documents if any
210     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
211     const std::set<std::string> aSubs = subDocuments(false);
212     std::set<std::string>::iterator aSubIter = aSubs.begin();
213     for (; aSubIter != aSubs.end() && isDone; aSubIter++) {
214       if (anApp->isLoadByDemand(*aSubIter)) { 
215         // copy not-activated document that is not in the memory
216         std::string aDocName = *aSubIter;
217         if (!aDocName.empty()) {
218           // just copy file
219           TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
220           OSD_Path aPath(aSubPath);
221           OSD_File aFile(aPath);
222           if (aFile.Exists()) {
223             TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName));
224             OSD_Path aDestination(aDestinationDir);
225             aFile.Copy(aDestination);
226             theResults.push_back(aDestinationDir.ToCString());
227           } else {
228             Events_Error::send(
229               std::string("Can not open file ") + aSubPath.ToCString() + " for saving");
230           }
231         }
232       } else { // simply save opened document
233         isDone = subDoc(*aSubIter)->save(theFileName, theResults);
234       }
235     }
236   }
237   return isDone;
238 }
239
240 void Model_Document::close(const bool theForever)
241 {
242   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
243   if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) {
244     aPM->setActiveDocument(aPM->moduleDocument());
245   }
246   // close all subs
247   const std::set<std::string> aSubs = subDocuments(true);
248   std::set<std::string>::iterator aSubIter = aSubs.begin();
249   for (; aSubIter != aSubs.end(); aSubIter++)
250     subDoc(*aSubIter)->close(theForever);
251
252   // close for thid document needs no transaction in this document
253   if (this == aPM->moduleDocument().get())
254     std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
255
256   // delete all features of this document
257   std::shared_ptr<ModelAPI_Document> aThis = 
258     Model_Application::getApplication()->getDocument(myID);
259   Events_Loop* aLoop = Events_Loop::loop();
260   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeaturesIter(myObjs);
261   for(; aFeaturesIter.More(); aFeaturesIter.Next()) {
262     FeaturePtr aFeature = aFeaturesIter.Value();
263     static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
264     ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
265     ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
266     aFeature->eraseResults();
267     if (theForever) { // issue #294: do not delete content of the document until it can be redone
268       aFeature->erase();
269     } else {
270       aFeature->data()->execState(ModelAPI_StateMustBeUpdated);
271     }
272   }
273   if (theForever) {
274     myObjs.Clear();
275   }
276   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
277   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
278
279   // close all only if it is really asked, otherwise it can be undoed/redoed
280   if (theForever) {
281     if (myDoc->CanClose() == CDM_CCS_OK)
282       myDoc->Close();
283   }
284
285   if (this == aPM->moduleDocument().get())
286     std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(true);
287 }
288
289 void Model_Document::startOperation()
290 {
291   if (myDoc->HasOpenCommand()) {  // start of nested command
292     if (myDoc->CommitCommand()) { // commit the current: it will contain all nested after compactification
293       (*myTransactions.rbegin())++; // if has open command, the list is not empty
294     }
295     myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet
296     myDoc->OpenCommand();
297   } else {  // start the simple command
298     myDoc->NewCommand();
299   }
300   // starts a new operation
301   myTransactions.push_back(0);
302   if (!myNestedNum.empty())
303     (*myNestedNum.rbegin())++;
304   myRedos.clear();
305   // new command for all subs
306   const std::set<std::string> aSubs = subDocuments(true);
307   std::set<std::string>::iterator aSubIter = aSubs.begin();
308   for (; aSubIter != aSubs.end(); aSubIter++)
309     subDoc(*aSubIter)->startOperation();
310 }
311
312 void Model_Document::compactNested()
313 {
314   if (!myNestedNum.empty()) {
315     int aNumToCompact = *(myNestedNum.rbegin());
316     int aSumOfTransaction = 0;
317     for(int a = 0; a < aNumToCompact; a++) {
318       aSumOfTransaction += *(myTransactions.rbegin());
319       myTransactions.pop_back();
320     }
321     // the latest transaction is the start of lower-level operation which startes the nested
322     *(myTransactions.rbegin()) += aSumOfTransaction;
323     myNestedNum.pop_back();
324   }
325 }
326
327 void Model_Document::finishOperation()
328 {
329   bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
330   static std::shared_ptr<Model_Session> aSession = 
331     std::static_pointer_cast<Model_Session>(Model_Session::get());
332   synchronizeBackRefs();
333   Events_Loop* aLoop = Events_Loop::loop();
334   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
335   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
336   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
337   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE));
338   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
339   // this must be here just after everything is finished but before real transaction stop
340   // to avoid messages about modifications outside of the transaction
341   // and to rebuild everything after all updates and creates
342   if (Model_Session::get()->moduleDocument().get() == this) { // once for root document
343     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
344     static std::shared_ptr<Events_Message> aFinishMsg
345       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
346     Events_Loop::loop()->send(aFinishMsg);
347     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
348   }
349   // to avoid "updated" message appearance by updater
350   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
351
352   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
353   const std::set<std::string> aSubs = subDocuments(true);
354   std::set<std::string>::iterator aSubIter = aSubs.begin();
355   for (; aSubIter != aSubs.end(); aSubIter++)
356     subDoc(*aSubIter)->finishOperation();
357
358   if (myDoc->CommitCommand()) { // if commit is successfull, just increment counters
359     (*myTransactions.rbegin())++;
360   }
361
362   if (isNestedClosed) {
363     compactNested();
364   }
365 }
366
367 void Model_Document::abortOperation()
368 {
369   if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
370     compactNested();
371     undoInternal(false);
372     myDoc->ClearRedos();
373     myRedos.clear();
374   } else { // abort the current
375     int aNumTransactions = *myTransactions.rbegin();
376     myTransactions.pop_back();
377     if (!myNestedNum.empty())
378       (*myNestedNum.rbegin())--;
379     // roll back the needed number of transactions
380     myDoc->AbortCommand();
381     for(int a = 0; a < aNumTransactions; a++)
382       myDoc->Undo();
383     myDoc->ClearRedos();
384   }
385   synchronizeFeatures(true, false); // references were not changed since transaction start
386   // abort for all subs
387   const std::set<std::string> aSubs = subDocuments(true);
388   std::set<std::string>::iterator aSubIter = aSubs.begin();
389   for (; aSubIter != aSubs.end(); aSubIter++)
390     subDoc(*aSubIter)->abortOperation();
391 }
392
393 bool Model_Document::isOperation()
394 {
395   // operation is opened for all documents: no need to check subs
396   return myDoc->HasOpenCommand() == Standard_True ;
397 }
398
399 bool Model_Document::isModified()
400 {
401   // is modified if at least one operation was commited and not undoed
402   return myTransactions.size() != myTransactionSave || isOperation();
403 }
404
405 bool Model_Document::canUndo()
406 {
407   if (myDoc->GetAvailableUndos() > 0 && (myNestedNum.empty() || *myNestedNum.rbegin() != 0) &&
408       !myTransactions.empty() /* for omitting the first useless transaction */)
409     return true;
410   // check other subs contains operation that can be undoed
411   const std::set<std::string> aSubs = subDocuments(true);
412   std::set<std::string>::iterator aSubIter = aSubs.begin();
413   for (; aSubIter != aSubs.end(); aSubIter++)
414     if (subDoc(*aSubIter)->canUndo())
415       return true;
416   return false;
417 }
418
419 void Model_Document::undoInternal(const bool theWithSubs)
420 {
421   int aNumTransactions = *myTransactions.rbegin();
422   myTransactions.pop_back();
423   myRedos.push_back(aNumTransactions);
424   if (!myNestedNum.empty())
425     (*myNestedNum.rbegin())--;
426   // roll back the needed number of transactions
427   for(int a = 0; a < aNumTransactions; a++)
428     myDoc->Undo();
429
430   if (theWithSubs) {
431     synchronizeFeatures(true, true);
432     // undo for all subs
433     const std::set<std::string> aSubs = subDocuments(true);
434     std::set<std::string>::iterator aSubIter = aSubs.begin();
435     for (; aSubIter != aSubs.end(); aSubIter++)
436       subDoc(*aSubIter)->undo();
437   }
438 }
439
440 void Model_Document::undo()
441 {
442   undoInternal(true);
443 }
444
445 bool Model_Document::canRedo()
446 {
447   if (myDoc->GetAvailableRedos() > 0)
448     return true;
449   // check other subs contains operation that can be redoed
450   const std::set<std::string> aSubs = subDocuments(true);
451   std::set<std::string>::iterator aSubIter = aSubs.begin();
452   for (; aSubIter != aSubs.end(); aSubIter++)
453     if (subDoc(*aSubIter)->canRedo())
454       return true;
455   return false;
456 }
457
458 void Model_Document::redo()
459 {
460   if (!myNestedNum.empty())
461     (*myNestedNum.rbegin())++;
462   int aNumRedos = *myRedos.rbegin();
463   myRedos.pop_back();
464   myTransactions.push_back(aNumRedos);
465   for(int a = 0; a < aNumRedos; a++)
466     myDoc->Redo();
467
468   synchronizeFeatures(true, true);
469   // redo for all subs
470   const std::set<std::string> aSubs = subDocuments(true);
471   std::set<std::string>::iterator aSubIter = aSubs.begin();
472   for (; aSubIter != aSubs.end(); aSubIter++)
473     subDoc(*aSubIter)->redo();
474 }
475
476 /// Append to the array of references a new referenced label
477 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced)
478 {
479   Handle(TDataStd_ReferenceArray) aRefs;
480   if (!theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
481     aRefs = TDataStd_ReferenceArray::Set(theArrayLab, 0, 0);
482     aRefs->SetValue(0, theReferenced);
483   } else {  // extend array by one more element
484     Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
485                                                                         aRefs->Upper() + 1);
486     for (int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
487       aNewArray->SetValue(a, aRefs->Value(a));
488     }
489     aNewArray->SetValue(aRefs->Upper() + 1, theReferenced);
490     aRefs->SetInternalArray(aNewArray);
491   }
492 }
493
494 FeaturePtr Model_Document::addFeature(std::string theID)
495 {
496   TDF_Label anEmptyLab;
497   FeaturePtr anEmptyFeature;
498   FeaturePtr aFeature = ModelAPI_Session::get()->createFeature(theID);
499   if (!aFeature)
500     return aFeature;
501   std::shared_ptr<Model_Document> aDocToAdd = std::dynamic_pointer_cast<Model_Document>(
502       aFeature->documentToAdd());
503   if (aFeature) {
504     TDF_Label aFeatureLab;
505     if (!aFeature->isAction()) {  // do not add action to the data model
506       TDF_Label aFeaturesLab = aDocToAdd->featuresLabel();
507       aFeatureLab = aFeaturesLab.NewChild();
508       aDocToAdd->initData(aFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS);
509       // keep the feature ID to restore document later correctly
510       TDataStd_Comment::Set(aFeatureLab, aFeature->getKind().c_str());
511       aDocToAdd->myObjs.Bind(aFeatureLab, aFeature);
512       // store feature in the history of features array
513       if (aFeature->isInHistory()) {
514         AddToRefArray(aFeaturesLab, aFeatureLab);
515       }
516     }
517     if (!aFeature->isAction()) {  // do not add action to the data model
518       // event: feature is added
519       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
520       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
521     } else { // feature must be executed
522        // no creation event => updater not working, problem with remove part
523       aFeature->execute();
524     }
525   }
526   return aFeature;
527 }
528
529 /// Appenad to the array of references a new referenced label.
530 /// If theIndex is not -1, removes element at thisindex, not theReferenced.
531 /// \returns the index of removed element
532 static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex =
533                                   -1)
534 {
535   int aResult = -1;  // no returned
536   Handle(TDataStd_ReferenceArray) aRefs;
537   if (theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
538     if (aRefs->Length() == 1) {  // just erase an array
539       if ((theIndex == -1 && aRefs->Value(0) == theReferenced) || theIndex == 0) {
540         theArrayLab.ForgetAttribute(TDataStd_ReferenceArray::GetID());
541       }
542       aResult = 0;
543     } else {  // reduce the array
544       Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
545                                                                           aRefs->Upper() - 1);
546       int aCount = aRefs->Lower();
547       for (int a = aCount; a <= aRefs->Upper(); a++, aCount++) {
548         if ((theIndex == -1 && aRefs->Value(a) == theReferenced) || theIndex == a) {
549           aCount--;
550           aResult = a;
551         } else {
552           aNewArray->SetValue(aCount, aRefs->Value(a));
553         }
554       }
555       aRefs->SetInternalArray(aNewArray);
556     }
557   }
558   return aResult;
559 }
560
561 void Model_Document::removeFeature(FeaturePtr theFeature, const bool theCheck)
562 {
563   if (theCheck) {
564     // check the feature: it must have no depended objects on it
565     std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
566     for(; aResIter != theFeature->results().cend(); aResIter++) {
567       std::shared_ptr<Model_Data> aData = 
568         std::dynamic_pointer_cast<Model_Data>((*aResIter)->data());
569       if (aData && !aData->refsToMe().empty()) {
570         Events_Error::send(
571           "Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
572         return;
573       }
574     }
575   }
576
577   std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theFeature->data());
578   if (aData) {
579     TDF_Label aFeatureLabel = aData->label().Father();
580     if (myObjs.IsBound(aFeatureLabel))
581       myObjs.UnBind(aFeatureLabel);
582     else
583       return;  // not found feature => do not remove
584     // erase fields
585     theFeature->erase();
586     // erase all attributes under the label of feature
587     aFeatureLabel.ForgetAllAttributes();
588     // remove it from the references array
589     if (theFeature->isInHistory()) {
590       RemoveFromRefArray(featuresLabel(), aFeatureLabel);
591     }
592   }
593   // event: feature is deleted
594   ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
595 }
596
597 FeaturePtr Model_Document::feature(TDF_Label& theLabel) const
598 {
599   if (myObjs.IsBound(theLabel))
600     return myObjs.Find(theLabel);
601   return FeaturePtr();  // not found
602 }
603
604 ObjectPtr Model_Document::object(TDF_Label theLabel)
605 {
606   // try feature by label
607   FeaturePtr aFeature = feature(theLabel);
608   if (aFeature)
609     return feature(theLabel);
610   TDF_Label aFeatureLabel = theLabel.Father().Father();  // let's suppose it is result
611   aFeature = feature(aFeatureLabel);
612   if (aFeature) {
613     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
614     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.cbegin();
615     for (; aRIter != aResults.cend(); aRIter++) {
616       std::shared_ptr<Model_Data> aResData = std::dynamic_pointer_cast<Model_Data>(
617           (*aRIter)->data());
618       if (aResData->label().Father().IsEqual(theLabel))
619         return *aRIter;
620     }
621   }
622   return FeaturePtr();  // not found
623 }
624
625 std::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
626 {
627   return Model_Application::getApplication()->getDocument(theDocID);
628 }
629
630 const std::set<std::string> Model_Document::subDocuments(const bool theActivatedOnly) const
631 {
632   std::set<std::string> aResult;
633   // comment must be in any feature: it is kind
634   int anIndex = 0;
635   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
636   for (; aLabIter.More(); aLabIter.Next()) {
637     TDF_Label aFLabel = aLabIter.Value()->Label();
638     FeaturePtr aFeature = feature(aFLabel);
639     if (aFeature.get()) { // if document is closed the feature may be not in myObjs map
640       const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
641       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
642       for (; aRIter != aResults.cend(); aRIter++) {
643         if ((*aRIter)->groupName() != ModelAPI_ResultPart::group()) continue;
644         if ((*aRIter)->isInHistory()) {
645           ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aRIter);
646           if (aPart && (!theActivatedOnly || aPart->isActivated()))
647             aResult.insert(aPart->data()->name());
648         }
649       }
650     }
651   }
652   return aResult;
653 }
654
655 std::shared_ptr<Model_Document> Model_Document::subDoc(std::string theDocID)
656 {
657   // just store sub-document identifier here to manage it later
658   return std::dynamic_pointer_cast<Model_Document>(
659     Model_Application::getApplication()->getDocument(theDocID));
660 }
661
662 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex,
663                                  const bool theHidden)
664 {
665   if (theGroupID == ModelAPI_Feature::group()) {
666     if (theHidden) {
667       int anIndex = 0;
668       TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
669       for (; aLabIter.More(); aLabIter.Next()) {
670         if (theIndex == anIndex) {
671           TDF_Label aFLabel = aLabIter.Value()->Label();
672           return feature(aFLabel);
673         }
674         anIndex++;
675       }
676     } else {
677       Handle(TDataStd_ReferenceArray) aRefs;
678       if (!featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
679         return ObjectPtr();
680       if (aRefs->Lower() > theIndex || aRefs->Upper() < theIndex)
681         return ObjectPtr();
682       TDF_Label aFeatureLabel = aRefs->Value(theIndex);
683       return feature(aFeatureLabel);
684     }
685   } else {
686     // comment must be in any feature: it is kind
687     int anIndex = 0;
688     TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
689     for (; aLabIter.More(); aLabIter.Next()) {
690       TDF_Label aFLabel = aLabIter.Value()->Label();
691       FeaturePtr aFeature = feature(aFLabel);
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         if ((*aRIter)->groupName() != theGroupID) continue;
696         bool isIn = theHidden && (*aRIter)->isInHistory();
697         if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result
698           isIn = !(*aRIter)->isConcealed();
699         }
700         if (isIn) {
701           if (anIndex == theIndex)
702             return *aRIter;
703           anIndex++;
704         }
705       }
706     }
707   }
708   // not found
709   return ObjectPtr();
710 }
711
712 int Model_Document::size(const std::string& theGroupID, const bool theHidden)
713 {
714   int aResult = 0;
715   if (theGroupID == ModelAPI_Feature::group()) {
716     if (theHidden) {
717       return myObjs.Size();
718     } else {
719       Handle(TDataStd_ReferenceArray) aRefs;
720       if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
721         return aRefs->Length();
722     }
723   } else {
724     // comment must be in any feature: it is kind
725     TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
726     for (; aLabIter.More(); aLabIter.Next()) {
727       TDF_Label aFLabel = aLabIter.Value()->Label();
728       FeaturePtr aFeature = feature(aFLabel);
729       if (!aFeature) // may be on close
730         continue;
731       const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
732       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
733       for (; aRIter != aResults.cend(); aRIter++) {
734         if ((*aRIter)->groupName() != theGroupID) continue;
735         bool isIn = theHidden;
736         if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result
737           isIn = !(*aRIter)->isConcealed();
738         }
739         if (isIn)
740           aResult++;
741       }
742     }
743   }
744   // group is not found
745   return aResult;
746 }
747
748 TDF_Label Model_Document::featuresLabel() const
749 {
750   return myDoc->Main().FindChild(TAG_OBJECTS);
751 }
752
753 void Model_Document::setUniqueName(FeaturePtr theFeature)
754 {
755   if (!theFeature->data()->name().empty())
756     return;  // not needed, name is already defined
757   std::string aName;  // result
758   // first count all objects of such kind to start with index = count + 1
759   int aNumObjects = 0;
760   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myObjs);
761   for (; aFIter.More(); aFIter.Next()) {
762     if (aFIter.Value()->getKind() == theFeature->getKind())
763       aNumObjects++;
764   }
765   // generate candidate name
766   std::stringstream aNameStream;
767   aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
768   aName = aNameStream.str();
769   // check this is unique, if not, increase index by 1
770   for (aFIter.Initialize(myObjs); aFIter.More();) {
771     FeaturePtr aFeature = aFIter.Value();
772     bool isSameName = aFeature->data()->name() == aName;
773     if (!isSameName) {  // check also results to avoid same results names (actual for Parts)
774       const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
775       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
776       for (; aRIter != aResults.cend(); aRIter++) {
777         isSameName = (*aRIter)->data()->name() == aName;
778       }
779     }
780     if (isSameName) {
781       aNumObjects++;
782       std::stringstream aNameStream;
783       aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
784       aName = aNameStream.str();
785       // reinitialize iterator to make sure a new name is unique
786       aFIter.Initialize(myObjs);
787     } else
788       aFIter.Next();
789   }
790   theFeature->data()->setName(aName);
791 }
792
793 void Model_Document::initData(ObjectPtr theObj, TDF_Label theLab, const int theTag)
794 {
795   std::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(
796       myID);
797   std::shared_ptr<Model_Data> aData(new Model_Data);
798   aData->setLabel(theLab.FindChild(theTag));
799   aData->setObject(theObj);
800   theObj->setDoc(aThis);
801   theObj->setData(aData);
802   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
803   if (aFeature) {
804     setUniqueName(aFeature);  // must be before "initAttributes" because duplicate part uses name
805     aFeature->initAttributes();
806   }
807 }
808
809 void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool theUpdateReferences)
810 {
811   std::shared_ptr<ModelAPI_Document> aThis = 
812     Model_Application::getApplication()->getDocument(myID);
813   // after all updates, sends a message that groups of features were created or updated
814   Events_Loop* aLoop = Events_Loop::loop();
815   aLoop->activateFlushes(false);
816
817   // update all objects by checking are they of labels or not
818   std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
819   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
820   for (; aLabIter.More(); aLabIter.Next()) {
821     TDF_Label aFeatureLabel = aLabIter.Value()->Label();
822     FeaturePtr aFeature;
823     if (!myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
824       // create a feature
825       aFeature = ModelAPI_Session::get()->createFeature(
826           TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
827               .ToCString());
828       if (!aFeature) {  // somethig is wrong, most probably, the opened document has invalid structure
829         Events_Error::send("Invalid type of object in the document");
830         aLabIter.Value()->Label().ForgetAllAttributes();
831         continue;
832       }
833       // this must be before "setData" to redo the sketch line correctly
834       myObjs.Bind(aFeatureLabel, aFeature);
835       aNewFeatures.insert(aFeature);
836       initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS);
837
838       // event: model is updated
839       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
840       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
841     } else {  // nothing is changed, both iterators are incremented
842       aFeature = myObjs.Find(aFeatureLabel);
843       aKeptFeatures.insert(aFeature);
844       if (theMarkUpdated) {
845         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
846         ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
847       }
848     }
849   }
850   // update results of thefeatures (after features created because they may be connected, like sketch and sub elements)
851   std::list<FeaturePtr> aComposites; // composites must be updated after their subs (issue 360)
852   TDF_ChildIDIterator aLabIter2(featuresLabel(), TDataStd_Comment::GetID());
853   for (; aLabIter2.More(); aLabIter2.Next()) {
854     TDF_Label aFeatureLabel = aLabIter2.Value()->Label();
855     if (myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
856       FeaturePtr aFeature = myObjs.Find(aFeatureLabel);
857       if (std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature).get())
858         aComposites.push_back(aFeature);
859       updateResults(aFeature);
860     }
861   }
862   std::list<FeaturePtr>::iterator aComposite = aComposites.begin();
863   for(; aComposite != aComposites.end(); aComposite++) {
864     updateResults(*aComposite);
865   }
866
867   // check all features are checked: if not => it was removed
868   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myObjs);
869   while (aFIter.More()) {
870     if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end()
871         && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
872       FeaturePtr aFeature = aFIter.Value();
873       // event: model is updated
874       //if (aFeature->isInHistory()) {
875         ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
876       //}
877       // results of this feature must be redisplayed (hided)
878       static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
879       const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
880       std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
881       // redisplay also removed feature (used for sketch and AISObject)
882       ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
883       aFeature->erase();
884       // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter
885       myObjs.UnBind(aFIter.Key());
886       // reinitialize iterator because unbind may corrupt the previous order in the map
887       aFIter.Initialize(myObjs);
888     } else
889       aFIter.Next();
890   }
891
892   if (theUpdateReferences) {
893     synchronizeBackRefs();
894   }
895
896   myExecuteFeatures = false;
897   aLoop->activateFlushes(true);
898
899   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
900   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
901   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
902   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
903   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE));
904   myExecuteFeatures = true;
905 }
906
907 void Model_Document::synchronizeBackRefs()
908 {
909   std::shared_ptr<ModelAPI_Document> aThis = 
910     Model_Application::getApplication()->getDocument(myID);
911   // keeps the concealed flags of result to catch the change and create created/deleted events
912   std::list<std::pair<ResultPtr, bool> > aConcealed;
913   // first cycle: erase all data about back-references
914   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeatures(myObjs);
915   for(; aFeatures.More(); aFeatures.Next()) {
916     FeaturePtr aFeature = aFeatures.Value();
917     std::shared_ptr<Model_Data> aFData = 
918       std::dynamic_pointer_cast<Model_Data>(aFeature->data());
919     if (aFData) {
920       aFData->eraseBackReferences();
921     }
922     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
923     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
924     for (; aRIter != aResults.cend(); aRIter++) {
925       std::shared_ptr<Model_Data> aResData = 
926         std::dynamic_pointer_cast<Model_Data>((*aRIter)->data());
927       if (aResData) {
928         aConcealed.push_back(std::pair<ResultPtr, bool>(*aRIter, (*aRIter)->isConcealed()));
929         aResData->eraseBackReferences();
930       }
931     }
932   }
933
934   // second cycle: set new back-references: only features may have reference, iterate only them
935   ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
936   for(aFeatures.Initialize(myObjs); aFeatures.More(); aFeatures.Next()) {
937     FeaturePtr aFeature = aFeatures.Value();
938     std::shared_ptr<Model_Data> aFData = 
939       std::dynamic_pointer_cast<Model_Data>(aFeature->data());
940     if (aFData) {
941       std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
942       aFData->referencesToObjects(aRefs);
943       std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator aRefsIter = aRefs.begin();
944       for(; aRefsIter != aRefs.end(); aRefsIter++) {
945         std::list<ObjectPtr>::iterator aRefTo = aRefsIter->second.begin();
946         for(; aRefTo != aRefsIter->second.end(); aRefTo++) {
947           if (*aRefTo) {
948             std::shared_ptr<Model_Data> aRefData = 
949               std::dynamic_pointer_cast<Model_Data>((*aRefTo)->data());
950             aRefData->addBackReference(aFeature, aRefsIter->first); // here the Concealed flag is updated
951           }
952         }
953       }
954     }
955   }
956   std::list<std::pair<ResultPtr, bool> >::iterator aCIter = aConcealed.begin();
957   for(; aCIter != aConcealed.end(); aCIter++) {
958     if (aCIter->first->isConcealed() != aCIter->second) { // somethign is changed => produce event
959       if (aCIter->second) { // was concealed become not => creation event
960         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
961         ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, anEvent);
962       } else { // was not concealed become concealed => delete event
963         ModelAPI_EventCreator::get()->sendDeleted(aThis, aCIter->first->groupName());
964         // redisplay for the viewer (it must be disappeared also)
965         static Events_ID EVENT_DISP = 
966           Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
967         ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, EVENT_DISP);
968       }
969     }
970   }
971 }
972
973 TDF_Label Model_Document::resultLabel(
974   const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theResultIndex) 
975 {
976   const std::shared_ptr<Model_Data>& aData = 
977     std::dynamic_pointer_cast<Model_Data>(theFeatureData);
978   return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1);
979 }
980
981 void Model_Document::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
982                                  std::shared_ptr<ModelAPI_Result> theResult,
983                                  const int theResultIndex)
984 {
985   std::shared_ptr<ModelAPI_Document> aThis = 
986     Model_Application::getApplication()->getDocument(myID);
987   theResult->setDoc(aThis);
988   initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS);
989   if (theResult->data()->name().empty()) {  // if was not initialized, generate event and set a name
990     theResult->data()->setName(theFeatureData->name());
991   }
992 }
993
994 std::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
995     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
996 {
997   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
998   TDataStd_Comment::Set(aLab, ModelAPI_ResultConstruction::group().c_str());
999   ObjectPtr anOldObject = object(aLab);
1000   std::shared_ptr<ModelAPI_ResultConstruction> aResult;
1001   if (anOldObject) {
1002     aResult = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(anOldObject);
1003   }
1004   if (!aResult) {
1005     aResult = std::shared_ptr<ModelAPI_ResultConstruction>(new Model_ResultConstruction);
1006     storeResult(theFeatureData, aResult, theIndex);
1007   }
1008   return aResult;
1009 }
1010
1011 std::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
1012     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1013 {
1014   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
1015   TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str());
1016   ObjectPtr anOldObject = object(aLab);
1017   std::shared_ptr<ModelAPI_ResultBody> aResult;
1018   if (anOldObject) {
1019     aResult = std::dynamic_pointer_cast<ModelAPI_ResultBody>(anOldObject);
1020   }
1021   if (!aResult) {
1022     aResult = std::shared_ptr<ModelAPI_ResultBody>(new Model_ResultBody);
1023     storeResult(theFeatureData, aResult, theIndex);
1024   }
1025   return aResult;
1026 }
1027
1028 std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
1029     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1030 {
1031   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
1032   TDataStd_Comment::Set(aLab, ModelAPI_ResultPart::group().c_str());
1033   ObjectPtr anOldObject = object(aLab);
1034   std::shared_ptr<ModelAPI_ResultPart> aResult;
1035   if (anOldObject) {
1036     aResult = std::dynamic_pointer_cast<ModelAPI_ResultPart>(anOldObject);
1037   }
1038   if (!aResult) {
1039     aResult = std::shared_ptr<ModelAPI_ResultPart>(new Model_ResultPart);
1040     storeResult(theFeatureData, aResult, theIndex);
1041   }
1042   return aResult;
1043 }
1044
1045 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
1046     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
1047 {
1048   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
1049   TDataStd_Comment::Set(aLab, ModelAPI_ResultGroup::group().c_str());
1050   ObjectPtr anOldObject = object(aLab);
1051   std::shared_ptr<ModelAPI_ResultGroup> aResult;
1052   if (anOldObject) {
1053     aResult = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anOldObject);
1054   }
1055   if (!aResult) {
1056     aResult = std::shared_ptr<ModelAPI_ResultGroup>(new Model_ResultGroup(theFeatureData));
1057     storeResult(theFeatureData, aResult, theIndex);
1058   }
1059   return aResult;
1060 }
1061
1062 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
1063     const std::shared_ptr<ModelAPI_Result>& theResult)
1064 {
1065   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(theResult->data());
1066   if (aData) {
1067     TDF_Label aFeatureLab = aData->label().Father().Father().Father();
1068     return feature(aFeatureLab);
1069   }
1070   return FeaturePtr();
1071 }
1072
1073 void Model_Document::updateResults(FeaturePtr theFeature)
1074 {
1075   // for not persistent is will be done by parametric updater automatically
1076   //if (!theFeature->isPersistentResult()) return;
1077   // check the existing results and remove them if there is nothing on the label
1078   std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
1079   while(aResIter != theFeature->results().cend()) {
1080     ResultPtr aBody = std::dynamic_pointer_cast<ModelAPI_Result>(*aResIter);
1081     if (aBody) {
1082       if (!aBody->data()->isValid()) { 
1083         // found a disappeared result => remove it
1084         theFeature->removeResult(aBody);
1085         // start iterate from beginning because iterator is corrupted by removing
1086         aResIter = theFeature->results().cbegin();
1087         continue;
1088       }
1089     }
1090     aResIter++;
1091   }
1092   // it may be on undo
1093   if (!theFeature->data() || !theFeature->data()->isValid())
1094     return;
1095   // check that results are presented on all labels
1096   int aResSize = theFeature->results().size();
1097   TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father());
1098   for(; aLabIter.More(); aLabIter.Next()) {
1099     // here must be GUID of the feature
1100     int aResIndex = aLabIter.Value().Tag() - 1;
1101     ResultPtr aNewBody;
1102     if (aResSize <= aResIndex) {
1103       TDF_Label anArgLab = aLabIter.Value();
1104       Handle(TDataStd_Comment) aGroup;
1105       if (anArgLab.FindAttribute(TDataStd_Comment::GetID(), aGroup)) {
1106         if (aGroup->Get() == ModelAPI_ResultBody::group().c_str()) {
1107           aNewBody = createBody(theFeature->data(), aResIndex);
1108         } else if (aGroup->Get() == ModelAPI_ResultPart::group().c_str()) {
1109           aNewBody = createPart(theFeature->data(), aResIndex);
1110         } else if (aGroup->Get() == ModelAPI_ResultConstruction::group().c_str()) {
1111           theFeature->execute(); // construction shapes are needed for sketch solver
1112           break;
1113         } else if (aGroup->Get() == ModelAPI_ResultGroup::group().c_str()) {
1114           aNewBody = createGroup(theFeature->data(), aResIndex);
1115         } else {
1116           Events_Error::send(std::string("Unknown type of result is found in the document:") +
1117             TCollection_AsciiString(aGroup->Get()).ToCString());
1118         }
1119       }
1120       if (aNewBody) {
1121         theFeature->setResult(aNewBody, aResIndex);
1122       }
1123     }
1124   }
1125 }
1126
1127 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
1128 {
1129   return TDF_LabelMapHasher::HashCode(theLab, theUpper);
1130
1131 }
1132 Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
1133 {
1134   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
1135 }
1136
1137 void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName)
1138 {
1139   myNamingNames[theName] = theLabel;
1140 }
1141
1142 TDF_Label Model_Document::findNamingName(std::string theName)
1143 {
1144   std::map<std::string, TDF_Label>::iterator aFind = myNamingNames.find(theName);
1145   if (aFind == myNamingNames.end())
1146     return TDF_Label(); // not found
1147   return aFind->second;
1148 }
1149
1150 ResultPtr Model_Document::findByName(const std::string theName)
1151 {
1152   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator anObjIter(myObjs);
1153   for(; anObjIter.More(); anObjIter.Next()) {
1154     FeaturePtr& aFeature = anObjIter.ChangeValue();
1155     if (!aFeature) // may be on close
1156       continue;
1157     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
1158     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
1159     for (; aRIter != aResults.cend(); aRIter++) {
1160       if (aRIter->get() && (*aRIter)->data() && (*aRIter)->data()->isValid() &&
1161           (*aRIter)->data()->name() == theName) {
1162         return *aRIter;
1163       }
1164     }
1165   }
1166   // not found
1167   return ResultPtr();
1168 }