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