]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cpp
Salome HOME
5e3088a441d78b91126d89a9d957e558355a1289
[modules/shaper.git] / src / Model / Model_Document.cpp
1 // File:        Model_Document.cxx
2 // Created:     28 Feb 2014
3 // Author:      Mikhail PONIKAROV
4
5 #include <Model_Document.h>
6 #include <Model_Data.h>
7 #include <Model_Application.h>
8 #include <Model_PluginManager.h>
9 #include <Model_Events.h>
10 #include <Model_ResultPart.h>
11 #include <Model_ResultConstruction.h>
12 #include <Model_ResultBody.h>
13 #include <Events_Loop.h>
14 #include <Events_Error.h>
15
16 #include <TDataStd_Integer.hxx>
17 #include <TDataStd_Comment.hxx>
18 #include <TDF_ChildIDIterator.hxx>
19 #include <TDataStd_ReferenceArray.hxx>
20 #include <TDataStd_HLabelArray1.hxx>
21 #include <TDataStd_Name.hxx>
22 #include <TDF_Reference.hxx>
23 #include <TDF_ChildIDIterator.hxx>
24 #include <TDF_LabelMapHasher.hxx>
25
26 #include <climits>
27 #ifndef WIN32
28 #include <sys/stat.h>
29 #endif
30
31 #ifdef WIN32
32 # define _separator_ '\\'
33 #else
34 # define _separator_ '/'
35 #endif
36
37 static const int UNDO_LIMIT = 10;  // number of possible undo operations
38
39 static const int TAG_GENERAL = 1;  // general properties tag
40 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
41 static const int TAG_HISTORY = 3;  // tag of the history sub-tree (python dump)
42
43 // feature sub-labels
44 static const int TAG_FEATURE_ARGUMENTS = 1;  ///< where the arguments are located
45 static const int TAG_FEATURE_RESULTS = 2;  ///< where the results are located
46
47 Model_Document::Model_Document(const std::string theID)
48     : myID(theID),
49       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
50 {
51   myDoc->SetUndoLimit(UNDO_LIMIT);
52   myTransactionsAfterSave = 0;
53   myNestedNum = -1;
54   myExecuteFeatures = true;
55   //myDoc->SetNestedTransactionMode();
56   // to have something in the document and avoid empty doc open/save problem
57   // in transaction for nesting correct working
58   myDoc->NewCommand();
59   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
60   myDoc->CommitCommand();
61 }
62
63 /// Returns the file name of this document by the nameof directory and identifuer of a document
64 static TCollection_ExtendedString DocFileName(const char* theFileName, const std::string& theID)
65 {
66   TCollection_ExtendedString aPath((const Standard_CString) theFileName);
67   aPath += _separator_;
68   aPath += theID.c_str();
69   aPath += ".cbf";  // standard binary file extension
70   return aPath;
71 }
72
73 bool Model_Document::load(const char* theFileName)
74 {
75   Handle(Model_Application) anApp = Model_Application::getApplication();
76   if (this == Model_PluginManager::get()->rootDocument().get()) {
77     anApp->setLoadPath(theFileName);
78   }
79   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
80   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
81   try {
82     aStatus = anApp->Open(aPath, myDoc);
83   } catch (Standard_Failure) {
84     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
85     Events_Error::send(
86         std::string("Exception in opening of document: ") + aFail->GetMessageString());
87     return false;
88   }
89   bool isError = aStatus != PCDM_RS_OK;
90   if (isError) {
91     switch (aStatus) {
92       case PCDM_RS_UnknownDocument:
93         Events_Error::send(std::string("Can not open document: PCDM_RS_UnknownDocument"));
94         break;
95       case PCDM_RS_AlreadyRetrieved:
96         Events_Error::send(std::string("Can not open document: PCDM_RS_AlreadyRetrieved"));
97         break;
98       case PCDM_RS_AlreadyRetrievedAndModified:
99         Events_Error::send(
100             std::string("Can not open document: PCDM_RS_AlreadyRetrievedAndModified"));
101         break;
102       case PCDM_RS_NoDriver:
103         Events_Error::send(std::string("Can not open document: PCDM_RS_NoDriver"));
104         break;
105       case PCDM_RS_UnknownFileDriver:
106         Events_Error::send(std::string("Can not open document: PCDM_RS_UnknownFileDriver"));
107         break;
108       case PCDM_RS_OpenError:
109         Events_Error::send(std::string("Can not open document: PCDM_RS_OpenError"));
110         break;
111       case PCDM_RS_NoVersion:
112         Events_Error::send(std::string("Can not open document: PCDM_RS_NoVersion"));
113         break;
114       case PCDM_RS_NoModel:
115         Events_Error::send(std::string("Can not open document: PCDM_RS_NoModel"));
116         break;
117       case PCDM_RS_NoDocument:
118         Events_Error::send(std::string("Can not open document: PCDM_RS_NoDocument"));
119         break;
120       case PCDM_RS_FormatFailure:
121         Events_Error::send(std::string("Can not open document: PCDM_RS_FormatFailure"));
122         break;
123       case PCDM_RS_TypeNotFoundInSchema:
124         Events_Error::send(std::string("Can not open document: PCDM_RS_TypeNotFoundInSchema"));
125         break;
126       case PCDM_RS_UnrecognizedFileFormat:
127         Events_Error::send(std::string("Can not open document: PCDM_RS_UnrecognizedFileFormat"));
128         break;
129       case PCDM_RS_MakeFailure:
130         Events_Error::send(std::string("Can not open document: PCDM_RS_MakeFailure"));
131         break;
132       case PCDM_RS_PermissionDenied:
133         Events_Error::send(std::string("Can not open document: PCDM_RS_PermissionDenied"));
134         break;
135       case PCDM_RS_DriverFailure:
136         Events_Error::send(std::string("Can not open document: PCDM_RS_DriverFailure"));
137         break;
138       default:
139         Events_Error::send(std::string("Can not open document: unknown error"));
140         break;
141     }
142   }
143   if (!isError) {
144     myDoc->SetUndoLimit(UNDO_LIMIT);
145     synchronizeFeatures();
146   }
147   return !isError;
148 }
149
150 bool Model_Document::save(const char* theFileName)
151 {
152   // create a directory in the root document if it is not yet exist
153   if (this == Model_PluginManager::get()->rootDocument().get()) {
154 #ifdef WIN32
155     CreateDirectory(theFileName, NULL);
156 #else
157     mkdir(theFileName, 0x1ff);
158 #endif
159   }
160   // filename in the dir is id of document inside of the given directory
161   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
162   PCDM_StoreStatus aStatus;
163   try {
164     aStatus = Model_Application::getApplication()->SaveAs(myDoc, aPath);
165   } catch (Standard_Failure) {
166     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
167     Events_Error::send(
168         std::string("Exception in saving of document: ") + aFail->GetMessageString());
169     return false;
170   }
171   bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
172   if (!isDone) {
173     switch (aStatus) {
174       case PCDM_SS_DriverFailure:
175         Events_Error::send(std::string("Can not save document: PCDM_SS_DriverFailure"));
176         break;
177       case PCDM_SS_WriteFailure:
178         Events_Error::send(std::string("Can not save document: PCDM_SS_WriteFailure"));
179         break;
180       case PCDM_SS_Failure:
181       default:
182         Events_Error::send(std::string("Can not save document: PCDM_SS_Failure"));
183         break;
184     }
185   }
186   myTransactionsAfterSave = 0;
187   if (isDone) {  // save also sub-documents if any
188     std::set<std::string>::iterator aSubIter = mySubs.begin();
189     for (; aSubIter != mySubs.end() && isDone; aSubIter++)
190       isDone = subDocument(*aSubIter)->save(theFileName);
191   }
192   return isDone;
193 }
194
195 void Model_Document::close()
196 {
197   boost::shared_ptr<ModelAPI_PluginManager> aPM = Model_PluginManager::get();
198   if (this != aPM->rootDocument().get() && this == aPM->currentDocument().get()) {
199     aPM->setCurrentDocument(aPM->rootDocument());
200   }
201   // close all subs
202   std::set<std::string>::iterator aSubIter = mySubs.begin();
203   for (; aSubIter != mySubs.end(); aSubIter++)
204     subDocument(*aSubIter)->close();
205   mySubs.clear();
206   // close this
207   /* do not close because it can be undoed
208    if (myDoc->CanClose() == CDM_CCS_OK)
209    myDoc->Close();
210    Model_Application::getApplication()->deleteDocument(myID);
211    */
212 }
213
214 void Model_Document::startOperation()
215 {
216   if (myDoc->HasOpenCommand()) {  // start of nested command
217     if (myNestedNum == -1) {
218       myNestedNum = 0;
219       myDoc->InitDeltaCompaction();
220     }
221     myIsEmptyTr[myTransactionsAfterSave] = false;
222     myTransactionsAfterSave++;
223     myDoc->NewCommand();
224   } else {  // start the simple command
225     myDoc->NewCommand();
226   }
227   // new command for all subs
228   std::set<std::string>::iterator aSubIter = mySubs.begin();
229   for (; aSubIter != mySubs.end(); aSubIter++)
230     subDocument(*aSubIter)->startOperation();
231 }
232
233 void Model_Document::compactNested()
234 {
235   bool allWasEmpty = true;
236   while (myNestedNum != -1) {
237     myTransactionsAfterSave--;
238     if (!myIsEmptyTr[myTransactionsAfterSave]) {
239       allWasEmpty = false;
240     }
241     myIsEmptyTr.erase(myTransactionsAfterSave);
242     myNestedNum--;
243   }
244   myIsEmptyTr[myTransactionsAfterSave] = allWasEmpty;
245   myTransactionsAfterSave++;
246   myDoc->PerformDeltaCompaction();
247 }
248
249 void Model_Document::finishOperation()
250 {
251   // just to be sure that everybody knows that changes were performed
252
253   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
254     boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
255         ->setCheckTransactions(false);  // for nested transaction commit
256   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
257   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
258   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
259   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
260   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
261     boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
262         ->setCheckTransactions(true);  // for nested transaction commit
263
264   if (myNestedNum != -1)  // this nested transaction is owervritten
265     myNestedNum++;
266   if (!myDoc->HasOpenCommand()) {
267     if (myNestedNum != -1) {
268       myNestedNum--;
269       compactNested();
270     }
271   } else {
272     // returns false if delta is empty and no transaction was made
273     myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();  // && (myNestedNum == -1);
274     myTransactionsAfterSave++;
275   }
276
277   // finish for all subs
278   std::set<std::string>::iterator aSubIter = mySubs.begin();
279   for (; aSubIter != mySubs.end(); aSubIter++)
280     subDocument(*aSubIter)->finishOperation();
281 }
282
283 void Model_Document::abortOperation()
284 {
285   if (myNestedNum > 0 && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
286       // first compact all nested
287     compactNested();
288     // for nested it is undo and clear redos
289     myDoc->Undo();
290     myDoc->ClearRedos();
291     myTransactionsAfterSave--;
292     myIsEmptyTr.erase(myTransactionsAfterSave);
293   } else {
294     if (myNestedNum == 0)  // abort only high-level
295       myNestedNum = -1;
296     myDoc->AbortCommand();
297   }
298   synchronizeFeatures(true);
299   // abort for all subs
300   std::set<std::string>::iterator aSubIter = mySubs.begin();
301   for (; aSubIter != mySubs.end(); aSubIter++)
302     subDocument(*aSubIter)->abortOperation();
303 }
304
305 bool Model_Document::isOperation()
306 {
307   // operation is opened for all documents: no need to check subs
308   return myDoc->HasOpenCommand() == Standard_True ;
309 }
310
311 bool Model_Document::isModified()
312 {
313   // is modified if at least one operation was commited and not undoed
314   return myTransactionsAfterSave > 0;
315 }
316
317 bool Model_Document::canUndo()
318 {
319   if (myDoc->GetAvailableUndos() > 0 && myNestedNum != 0
320       && myTransactionsAfterSave != 0 /* for omitting the first useless transaction */)
321     return true;
322   // check other subs contains operation that can be undoed
323   std::set<std::string>::iterator aSubIter = mySubs.begin();
324   for (; aSubIter != mySubs.end(); aSubIter++)
325     if (subDocument(*aSubIter)->canUndo())
326       return true;
327   return false;
328 }
329
330 void Model_Document::undo()
331 {
332   myTransactionsAfterSave--;
333   if (myNestedNum > 0)
334     myNestedNum--;
335   if (!myIsEmptyTr[myTransactionsAfterSave])
336     myDoc->Undo();
337   synchronizeFeatures(true);
338   // undo for all subs
339   std::set<std::string>::iterator aSubIter = mySubs.begin();
340   for (; aSubIter != mySubs.end(); aSubIter++)
341     subDocument(*aSubIter)->undo();
342 }
343
344 bool Model_Document::canRedo()
345 {
346   if (myDoc->GetAvailableRedos() > 0)
347     return true;
348   // check other subs contains operation that can be redoed
349   std::set<std::string>::iterator aSubIter = mySubs.begin();
350   for (; aSubIter != mySubs.end(); aSubIter++)
351     if (subDocument(*aSubIter)->canRedo())
352       return true;
353   return false;
354 }
355
356 void Model_Document::redo()
357 {
358   if (myNestedNum != -1)
359     myNestedNum++;
360   if (!myIsEmptyTr[myTransactionsAfterSave])
361     myDoc->Redo();
362   myTransactionsAfterSave++;
363   synchronizeFeatures(true);
364   // redo for all subs
365   std::set<std::string>::iterator aSubIter = mySubs.begin();
366   for (; aSubIter != mySubs.end(); aSubIter++)
367     subDocument(*aSubIter)->redo();
368 }
369
370 /// Appenad to the array of references a new referenced label
371 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced)
372 {
373   Handle(TDataStd_ReferenceArray) aRefs;
374   if (!theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
375     aRefs = TDataStd_ReferenceArray::Set(theArrayLab, 0, 0);
376     aRefs->SetValue(0, theReferenced);
377   } else {  // extend array by one more element
378     Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
379                                                                         aRefs->Upper() + 1);
380     for (int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
381       aNewArray->SetValue(a, aRefs->Value(a));
382     }
383     aNewArray->SetValue(aRefs->Upper() + 1, theReferenced);
384     aRefs->SetInternalArray(aNewArray);
385   }
386 }
387
388 FeaturePtr Model_Document::addFeature(std::string theID)
389 {
390   TDF_Label anEmptyLab;
391   FeaturePtr anEmptyFeature;
392   FeaturePtr aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
393   boost::shared_ptr<Model_Document> aDocToAdd = boost::dynamic_pointer_cast<Model_Document>(
394       aFeature->documentToAdd());
395   if (aFeature) {
396     TDF_Label aFeatureLab;
397     if (!aFeature->isAction()) {  // do not add action to the data model
398       TDF_Label aFeaturesLab = aDocToAdd->featuresLabel();
399       aFeatureLab = aFeaturesLab.NewChild();
400       aDocToAdd->initData(aFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS);
401       // keep the feature ID to restore document later correctly
402       TDataStd_Comment::Set(aFeatureLab, aFeature->getKind().c_str());
403       aDocToAdd->myObjs.Bind(aFeatureLab, aFeature);
404       // store feature in the history of features array
405       if (aFeature->isInHistory()) {
406         AddToRefArray(aFeaturesLab, aFeatureLab);
407       }
408     }
409     if (!aFeature->isAction()) {  // do not add action to the data model
410       // event: feature is added
411       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
412       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
413     }
414   }
415   return aFeature;
416 }
417
418 /// Appenad to the array of references a new referenced label.
419 /// If theIndex is not -1, removes element at thisindex, not theReferenced.
420 /// \returns the index of removed element
421 static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex =
422                                   -1)
423 {
424   int aResult = -1;  // no returned
425   Handle(TDataStd_ReferenceArray) aRefs;
426   if (theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
427     if (aRefs->Length() == 1) {  // just erase an array
428       if ((theIndex == -1 && aRefs->Value(0) == theReferenced) || theIndex == 0) {
429         theArrayLab.ForgetAttribute(TDataStd_ReferenceArray::GetID());
430       }
431       aResult = 0;
432     } else {  // reduce the array
433       Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(),
434                                                                           aRefs->Upper() - 1);
435       int aCount = aRefs->Lower();
436       for (int a = aCount; a <= aRefs->Upper(); a++, aCount++) {
437         if ((theIndex == -1 && aRefs->Value(a) == theReferenced) || theIndex == a) {
438           aCount--;
439           aResult = a;
440         } else {
441           aNewArray->SetValue(aCount, aRefs->Value(a));
442         }
443       }
444       aRefs->SetInternalArray(aNewArray);
445     }
446   }
447   return aResult;
448 }
449
450 void Model_Document::removeFeature(FeaturePtr theFeature)
451 {
452   boost::shared_ptr<Model_Data> aData = boost::static_pointer_cast<Model_Data>(theFeature->data());
453   TDF_Label aFeatureLabel = aData->label().Father();
454   if (myObjs.IsBound(aFeatureLabel))
455     myObjs.UnBind(aFeatureLabel);
456   else
457     return;  // not found feature => do not remove
458
459   // erase all attributes under the label of feature
460   aFeatureLabel.ForgetAllAttributes();
461   // remove it from the references array
462   RemoveFromRefArray(featuresLabel(), aFeatureLabel);
463
464   // event: feature is deleted
465   ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
466   // results of this feature must be redisplayed
467   static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
468   const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
469   std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
470   for (; aRIter != aResults.cend(); aRIter++) {
471     boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
472     aRes->setData(boost::shared_ptr<ModelAPI_Data>());  // deleted flag
473     ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
474     ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), aRes->groupName());
475   }
476 }
477
478 FeaturePtr Model_Document::feature(TDF_Label& theLabel)
479 {
480   if (myObjs.IsBound(theLabel))
481     return myObjs.Find(theLabel);
482   return FeaturePtr();  // not found
483 }
484
485 ObjectPtr Model_Document::object(TDF_Label theLabel)
486 {
487   // try feature by label
488   FeaturePtr aFeature = feature(theLabel);
489   if (aFeature)
490     return feature(theLabel);
491   TDF_Label aFeatureLabel = theLabel.Father().Father();  // let's suppose it is result
492   aFeature = feature(aFeatureLabel);
493   if (aFeature) {
494     const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
495     std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.cbegin();
496     for (; aRIter != aResults.cend(); aRIter++) {
497       boost::shared_ptr<Model_Data> aResData = boost::dynamic_pointer_cast<Model_Data>(
498           (*aRIter)->data());
499       if (aResData->label().Father().IsEqual(theLabel))
500         return *aRIter;
501     }
502   }
503   return FeaturePtr();  // not found
504 }
505
506 boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
507 {
508   // just store sub-document identifier here to manage it later
509   if (mySubs.find(theDocID) == mySubs.end())
510     mySubs.insert(theDocID);
511   return Model_Application::getApplication()->getDocument(theDocID);
512 }
513
514 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex,
515                                  const bool theHidden)
516 {
517   if (theGroupID == ModelAPI_Feature::group()) {
518     if (theHidden) {
519       int anIndex = 0;
520       TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
521       for (; aLabIter.More(); aLabIter.Next()) {
522         if (theIndex == anIndex) {
523           TDF_Label aFLabel = aLabIter.Value()->Label();
524           return feature(aFLabel);
525         }
526         anIndex++;
527       }
528     } else {
529       Handle(TDataStd_ReferenceArray) aRefs;
530       if (!featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
531         return ObjectPtr();
532       if (aRefs->Lower() > theIndex || aRefs->Upper() < theIndex)
533         return ObjectPtr();
534       TDF_Label aFeatureLabel = aRefs->Value(theIndex);
535       return feature(aFeatureLabel);
536     }
537   } else {
538     // comment must be in any feature: it is kind
539     int anIndex = 0;
540     TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
541     for (; aLabIter.More(); aLabIter.Next()) {
542       TDF_Label aFLabel = aLabIter.Value()->Label();
543       FeaturePtr aFeature = feature(aFLabel);
544       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
545       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
546       for (; aRIter != aResults.cend(); aRIter++) {
547         if ((theHidden || (*aRIter)->isInHistory()) && (*aRIter)->groupName() == theGroupID) {
548           if (anIndex == theIndex)
549             return *aRIter;
550           anIndex++;
551         }
552       }
553     }
554   }
555   // not found
556   return ObjectPtr();
557 }
558
559 int Model_Document::size(const std::string& theGroupID, const bool theHidden)
560 {
561   int aResult = 0;
562   if (theGroupID == ModelAPI_Feature::group()) {
563     if (theHidden) {
564       return myObjs.Size();
565     } else {
566       Handle(TDataStd_ReferenceArray) aRefs;
567       if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
568         return aRefs->Length();
569     }
570   } else {
571     // comment must be in any feature: it is kind
572     TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
573     for (; aLabIter.More(); aLabIter.Next()) {
574       TDF_Label aFLabel = aLabIter.Value()->Label();
575       FeaturePtr aFeature = feature(aFLabel);
576       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
577       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
578       for (; aRIter != aResults.cend(); aRIter++) {
579         if ((theHidden || (*aRIter)->isInHistory()) && (*aRIter)->groupName() == theGroupID) {
580           aResult++;
581         }
582       }
583     }
584   }
585   // group is not found
586   return aResult;
587 }
588
589 TDF_Label Model_Document::featuresLabel()
590 {
591   return myDoc->Main().FindChild(TAG_OBJECTS);
592 }
593
594 void Model_Document::setUniqueName(FeaturePtr theFeature)
595 {
596   if (!theFeature->data()->name().empty())
597     return;  // not needed, name is already defined
598   std::string aName;  // result
599   // first count all objects of such kind to start with index = count + 1
600   int aNumObjects = 0;
601   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myObjs);
602   for (; aFIter.More(); aFIter.Next()) {
603     if (aFIter.Value()->getKind() == theFeature->getKind())
604       aNumObjects++;
605   }
606   // generate candidate name
607   std::stringstream aNameStream;
608   aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
609   aName = aNameStream.str();
610   // check this is unique, if not, increase index by 1
611   for (aFIter.Initialize(myObjs); aFIter.More();) {
612     FeaturePtr aFeature = aFIter.Value();
613     bool isSameName = aFeature->isInHistory() && aFeature->data()->name() == aName;
614     if (!isSameName) {  // check also results to avoid same results names (actual for Parts)
615       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
616       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
617       for (; aRIter != aResults.cend(); aRIter++) {
618         isSameName = (*aRIter)->isInHistory() && (*aRIter)->data()->name() == aName;
619       }
620     }
621     if (isSameName) {
622       aNumObjects++;
623       std::stringstream aNameStream;
624       aNameStream << theFeature->getKind() << "_" << aNumObjects + 1;
625       aName = aNameStream.str();
626       // reinitialize iterator to make sure a new name is unique
627       aFIter.Initialize(myObjs);
628     } else
629       aFIter.Next();
630   }
631   theFeature->data()->setName(aName);
632 }
633
634 void Model_Document::initData(ObjectPtr theObj, TDF_Label theLab, const int theTag)
635 {
636   boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(
637       myID);
638   boost::shared_ptr<Model_Data> aData(new Model_Data);
639   aData->setLabel(theLab.FindChild(theTag));
640   aData->setObject(theObj);
641   theObj->setDoc(aThis);
642   theObj->setData(aData);
643   FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
644   if (aFeature) {
645     setUniqueName(aFeature);  // must be before "initAttributes" because duplicate part uses name
646     aFeature->initAttributes();
647   }
648 }
649
650 void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
651 {
652   boost::shared_ptr<ModelAPI_Document> aThis = 
653     Model_Application::getApplication()->getDocument(myID);
654   // after all updates, sends a message that groups of features were created or updated
655   boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
656     ->setCheckTransactions(false);
657   // update all objects by checking are they of labels or not
658   std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
659   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
660   for (; aLabIter.More(); aLabIter.Next()) {
661     TDF_Label aFeatureLabel = aLabIter.Value()->Label();
662     if (!myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
663       // create a feature
664       FeaturePtr aNewObj = ModelAPI_PluginManager::get()->createFeature(
665           TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
666               .ToCString());
667       if (!aNewObj) {  // somethig is wrong, most probably, the opened document has invalid structure
668         Events_Error::send("Invalid type of object in the document");
669         aLabIter.Value()->Label().ForgetAllAttributes();
670         continue;
671       }
672       // this must be before "setData" to redo the sketch line correctly
673       myObjs.Bind(aFeatureLabel, aNewObj);
674       aNewFeatures.insert(aNewObj);
675       initData(aNewObj, aFeatureLabel, TAG_FEATURE_ARGUMENTS);
676
677       // event: model is updated
678       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
679       ModelAPI_EventCreator::get()->sendUpdated(aNewObj, anEvent);
680
681       // update results of the appeared feature
682       updateResults(aNewObj);
683     } else {  // nothing is changed, both iterators are incremented
684       FeaturePtr aFeature = myObjs.Find(aFeatureLabel);
685       aKeptFeatures.insert(aFeature);
686       if (theMarkUpdated) {
687         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
688         ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
689       }
690       updateResults(aFeature);
691     }
692   }
693   // execute new features to restore results: after features creation to make all references valid
694   /*std::set<FeaturePtr>::iterator aNewIter = aNewFeatures.begin();
695    for(; aNewIter != aNewFeatures.end(); aNewIter++) {
696    (*aNewIter)->execute();
697    }*/
698   // check all features are checked: if not => it was removed
699   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myObjs);
700   while (aFIter.More()) {
701     if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end()
702         && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
703       FeaturePtr aFeature = aFIter.Value();
704       TDF_Label aLab = aFIter.Key();
705       aFIter.Next();
706       myObjs.UnBind(aLab);
707       // event: model is updated
708       if (aFeature->isInHistory()) {
709         ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
710       }
711       // results of this feature must be redisplayed (hided)
712       static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
713       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
714       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
715       for (; aRIter != aResults.cend(); aRIter++) {
716         boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
717         //aRes->setData(boost::shared_ptr<ModelAPI_Data>()); // deleted flag
718         ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
719         ModelAPI_EventCreator::get()->sendDeleted(aThis, aRes->groupName());
720       }
721       // redisplay also removed feature (used for sketch and AISObject)
722       ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
723     } else
724       aFIter.Next();
725   }
726
727   myExecuteFeatures = false;
728   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
729   if (theMarkUpdated)
730     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
731   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
732   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
733   boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
734     ->setCheckTransactions(true);
735   myExecuteFeatures = true;
736 }
737
738 TDF_Label Model_Document::resultLabel(
739   const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theResultIndex) 
740 {
741   const boost::shared_ptr<Model_Data>& aData = 
742     boost::dynamic_pointer_cast<Model_Data>(theFeatureData);
743   return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1);
744 }
745
746 void Model_Document::storeResult(boost::shared_ptr<ModelAPI_Data> theFeatureData,
747                                  boost::shared_ptr<ModelAPI_Result> theResult,
748                                  const int theResultIndex)
749 {
750   boost::shared_ptr<ModelAPI_Document> aThis = 
751     Model_Application::getApplication()->getDocument(myID);
752   theResult->setDoc(aThis);
753   initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS);
754   if (theResult->data()->name().empty()) {  // if was not initialized, generate event and set a name
755     theResult->data()->setName(theFeatureData->name());
756   }
757 }
758
759 boost::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
760     const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
761 {
762   ObjectPtr anOldObject = object(resultLabel(theFeatureData, theIndex));
763   boost::shared_ptr<ModelAPI_ResultConstruction> aResult;
764   if (anOldObject) {
765     aResult = boost::dynamic_pointer_cast<ModelAPI_ResultConstruction>(anOldObject);
766   }
767   if (!aResult) {
768     aResult = boost::shared_ptr<ModelAPI_ResultConstruction>(new Model_ResultConstruction);
769     storeResult(theFeatureData, aResult, theIndex);
770   }
771   return aResult;
772 }
773
774 boost::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
775     const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
776 {
777   ObjectPtr anOldObject = object(resultLabel(theFeatureData, theIndex));
778   boost::shared_ptr<ModelAPI_ResultBody> aResult;
779   if (anOldObject) {
780     aResult = boost::dynamic_pointer_cast<ModelAPI_ResultBody>(anOldObject);
781   }
782   if (!aResult) {
783     aResult = boost::shared_ptr<ModelAPI_ResultBody>(new Model_ResultBody);
784     storeResult(theFeatureData, aResult, theIndex);
785   }
786   return aResult;
787 }
788
789 boost::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
790     const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
791 {
792   ObjectPtr anOldObject = object(resultLabel(theFeatureData, theIndex));
793   boost::shared_ptr<ModelAPI_ResultPart> aResult;
794   if (anOldObject) {
795     aResult = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(anOldObject);
796   }
797   if (!aResult) {
798     aResult = boost::shared_ptr<ModelAPI_ResultPart>(new Model_ResultPart);
799     storeResult(theFeatureData, aResult, theIndex);
800   }
801   return aResult;
802 }
803
804 boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(
805     const boost::shared_ptr<ModelAPI_Result>& theResult)
806 {
807   boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>(theResult->data());
808   if (aData) {
809     TDF_Label aFeatureLab = aData->label().Father().Father().Father();
810     return feature(aFeatureLab);
811   }
812   return FeaturePtr();
813 }
814
815 void Model_Document::updateResults(FeaturePtr theFeature)
816 {
817   // for not persistent is will be done by parametric updater automatically
818   if (!theFeature->isPersistentResult()) return;
819   // check the existing results and remove them if there is nothing on the label
820   std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
821   while(aResIter != theFeature->results().cend()) {
822     ResultBodyPtr aBody = boost::dynamic_pointer_cast<ModelAPI_ResultBody>(*aResIter);
823     if (aBody) {
824       if (!aBody->data()->isValid()) { 
825         // found a disappeared result => remove it
826         theFeature->removeResult(aBody);
827         // start iterate from beginning because iterator is corrupted by removing
828         aResIter = theFeature->results().cbegin();
829         continue;
830       }
831     }
832     aResIter++;
833   }
834   // check that results are presented on all labels
835   int aResSize = theFeature->results().size();
836   TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father());
837   for(; aLabIter.More(); aLabIter.Next()) {
838     // here must be at least Name
839     int aResIndex = aLabIter.Value().Tag() - 1;
840     if (aLabIter.Value().FindChild(TAG_FEATURE_ARGUMENTS).HasAttribute() && 
841         aResSize <= aResIndex) 
842     {
843       ResultBodyPtr aNewBody = createBody(theFeature->data(), aResIndex);
844       theFeature->setResult(aNewBody, aResIndex);
845     }
846   }
847 }
848
849 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
850 {
851   return TDF_LabelMapHasher::HashCode(theLab, theUpper);
852
853 }
854 Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
855 {
856   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
857 }