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