]> SALOME platform Git repositories - modules/shaper.git/blob - src/Model/Model_Document.cpp
Salome HOME
210195844c039a2d894c5817cc4700bac4d14f5a
[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_DELETED));
226
227   if (myNestedNum != -1) // this nested transaction is owervritten
228     myNestedNum++;
229   if (!myDoc->HasOpenCommand()) {
230     if (myNestedNum != -1) {
231       myNestedNum--;
232       compactNested();
233     }
234   } else {
235     // returns false if delta is empty and no transaction was made
236     myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand() && (myNestedNum == -1);
237     myTransactionsAfterSave++;
238   }
239
240   // finish for all subs
241   std::set<std::string>::iterator aSubIter = mySubs.begin();
242   for(; aSubIter != mySubs.end(); aSubIter++)
243     subDocument(*aSubIter)->finishOperation();
244 }
245
246 void Model_Document::abortOperation()
247 {
248   if (myNestedNum > 0 && !myDoc->HasOpenCommand()) { // abort all what was done in nested
249     // first compact all nested
250     compactNested();
251     // for nested it is undo and clear redos
252     myDoc->Undo();
253     myDoc->ClearRedos();
254     myTransactionsAfterSave--;
255     myIsEmptyTr.erase(myTransactionsAfterSave);
256   } else {
257     if (myNestedNum == 0) // abort only high-level
258       myNestedNum = -1;
259     myDoc->AbortCommand();
260   }
261   synchronizeFeatures(true);
262   // abort for all subs
263   std::set<std::string>::iterator aSubIter = mySubs.begin();
264     for(; aSubIter != mySubs.end(); aSubIter++)
265     subDocument(*aSubIter)->abortOperation();
266 }
267
268 bool Model_Document::isOperation()
269 {
270   // operation is opened for all documents: no need to check subs
271   return myDoc->HasOpenCommand() == Standard_True;
272 }
273
274 bool Model_Document::isModified()
275 {
276   // is modified if at least one operation was commited and not undoed
277   return myTransactionsAfterSave > 0;
278 }
279
280 bool Model_Document::canUndo()
281 {
282   if (myDoc->GetAvailableUndos() > 0 && myNestedNum != 0 && myTransactionsAfterSave != 0 /* for omitting the first useless transaction */)
283     return true;
284   // check other subs contains operation that can be undoed
285   std::set<std::string>::iterator aSubIter = mySubs.begin();
286   for(; aSubIter != mySubs.end(); aSubIter++)
287     if (subDocument(*aSubIter)->canUndo())
288       return true;
289   return false;
290 }
291
292 void Model_Document::undo()
293 {
294   myTransactionsAfterSave--;
295   if (myNestedNum > 0) myNestedNum--;
296   if (!myIsEmptyTr[myTransactionsAfterSave])
297     myDoc->Undo();
298   synchronizeFeatures(true);
299   // undo for all subs
300   std::set<std::string>::iterator aSubIter = mySubs.begin();
301   for(; aSubIter != mySubs.end(); aSubIter++)
302     subDocument(*aSubIter)->undo();
303 }
304
305 bool Model_Document::canRedo()
306 {
307   if (myDoc->GetAvailableRedos() > 0)
308     return true;
309   // check other subs contains operation that can be redoed
310   std::set<std::string>::iterator aSubIter = mySubs.begin();
311   for(; aSubIter != mySubs.end(); aSubIter++)
312     if (subDocument(*aSubIter)->canRedo())
313       return true;
314   return false;
315 }
316
317 void Model_Document::redo()
318 {
319   if (myNestedNum != -1) myNestedNum++;
320   if (!myIsEmptyTr[myTransactionsAfterSave])
321     myDoc->Redo();
322   myTransactionsAfterSave++;
323   synchronizeFeatures(true);
324   // redo for all subs
325   std::set<std::string>::iterator aSubIter = mySubs.begin();
326   for(; aSubIter != mySubs.end(); aSubIter++)
327     subDocument(*aSubIter)->redo();
328 }
329
330 /// Appenad to the array of references a new referenced label
331 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced) {
332   Handle(TDataStd_ReferenceArray) aRefs;
333   if (!theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
334     aRefs = TDataStd_ReferenceArray::Set(theArrayLab, 0, 0);
335     aRefs->SetValue(0, theReferenced);
336   } else { // extend array by one more element
337     Handle(TDataStd_HLabelArray1) aNewArray = 
338       new TDataStd_HLabelArray1(aRefs->Lower(), aRefs->Upper() + 1);
339     for(int a = aRefs->Lower(); a <= aRefs->Upper(); a++) {
340       aNewArray->SetValue(a, aRefs->Value(a));
341     }
342     aNewArray->SetValue(aRefs->Upper() + 1, theReferenced);
343     aRefs->SetInternalArray(aNewArray);
344   }
345 }
346
347 FeaturePtr Model_Document::addFeature(std::string theID)
348 {
349   TDF_Label anEmptyLab;
350   FeaturePtr anEmptyFeature;
351   FeaturePtr aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
352   if (aFeature) {
353     TDF_Label aFeatureLab;
354     if (!aFeature->isAction()) {// do not add action to the data model
355       TDF_Label aFeaturesLab = groupLabel(ModelAPI_Feature::group());
356       aFeatureLab = aFeaturesLab.NewChild();
357       initData(aFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS);
358       // keep the feature ID to restore document later correctly
359       TDataStd_Comment::Set(aFeatureLab, aFeature->getKind().c_str());
360       setUniqueName(aFeature);
361       myObjs[ModelAPI_Feature::group()].push_back(aFeature);
362       // store feature in the history of features array
363       if (aFeature->isInHistory()) {
364         AddToRefArray(aFeaturesLab, aFeatureLab);
365       }
366     }
367     if (!aFeature->isAction()) {// do not add action to the data model
368       // event: feature is added
369       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
370       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
371     }
372   }
373   return aFeature;
374 }
375
376 /// Appenad to the array of references a new referenced label.
377 /// If theIndex is not -1, removes element at thisindex, not theReferenced.
378 /// \returns the index of removed element
379 static int RemoveFromRefArray(
380   TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex = -1) {
381   int aResult = -1; // no returned
382   Handle(TDataStd_ReferenceArray) aRefs;
383   if (theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
384     if (aRefs->Length() == 1) { // just erase an array
385       if ((theIndex == -1 && aRefs->Value(0) == theReferenced) || theIndex == 0) {
386         theArrayLab.ForgetAttribute(TDataStd_ReferenceArray::GetID());
387       }
388       aResult = 0;
389     } else { // reduce the array
390       Handle(TDataStd_HLabelArray1) aNewArray = 
391         new TDataStd_HLabelArray1(aRefs->Lower(), aRefs->Upper() - 1);
392       int aCount = aRefs->Lower();
393       for(int a = aCount; a <= aRefs->Upper(); a++, aCount++) {
394         if ((theIndex == -1 && aRefs->Value(a) == theReferenced) || theIndex == a) {
395           aCount--;
396           aResult = a;
397         } else {
398           aNewArray->SetValue(aCount, aRefs->Value(a));
399         }
400       }
401       aRefs->SetInternalArray(aNewArray);
402     }
403   }
404   return aResult;
405 }
406
407 void Model_Document::removeFeature(FeaturePtr theFeature)
408 {
409   boost::shared_ptr<Model_Data> aData = boost::static_pointer_cast<Model_Data>(theFeature->data());
410   TDF_Label aFeatureLabel = aData->label().Father();
411   // remove feature from the myObjects list
412   std::vector<ObjectPtr>& aVec = myObjs[ModelAPI_Feature::group()];
413   std::vector<ObjectPtr>::iterator anIter = aVec.begin();
414   while(anIter != aVec.end()) {
415     if (*anIter == theFeature) {
416       anIter = aVec.erase(anIter);
417     } else {
418       anIter++;
419     }
420   }
421   // erase all attributes under the label of feature
422   aFeatureLabel.ForgetAllAttributes();
423   // remove it from the references array
424   RemoveFromRefArray(groupLabel(ModelAPI_Feature::group()), aData->label());
425
426   // event: feature is deleted
427   ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
428   // results of this feature must be redisplayed
429   static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
430   const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
431   std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
432   for(; aRIter != aResults.cend(); aRIter++) {
433     boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
434     aRes->setData(boost::shared_ptr<ModelAPI_Data>()); // deleted flag
435     ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
436   }
437 }
438
439 /// returns the object group name by the object label
440 static std::string groupName(TDF_Label theObjectLabel) {
441   TDF_Label aGroupLab = theObjectLabel.Father();
442   Handle(TDataStd_Comment) aComment;
443   if (aGroupLab.FindAttribute(TDataStd_Comment::GetID(), aComment))
444     return std::string(TCollection_AsciiString(aComment->Get()).ToCString());
445   return ""; // not found
446 }
447
448 FeaturePtr Model_Document::feature(TDF_Label& theLabel)
449 {
450   // iterate all features, may be optimized later by keeping labels-map
451   std::vector<ObjectPtr>& aVec = myObjs[ModelAPI_Feature::group()];
452   std::vector<ObjectPtr>::iterator aFIter = aVec.begin();
453   for(; aFIter != aVec.end(); aFIter++) {
454     boost::shared_ptr<Model_Data> aData = 
455       boost::dynamic_pointer_cast<Model_Data>((*aFIter)->data());
456     if (aData->label().IsEqual(theLabel))
457       return boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter);
458   }
459   return FeaturePtr(); // not found
460 }
461
462 ObjectPtr Model_Document::object(TDF_Label& theLabel)
463 {
464   // iterate all features, may be optimized later by keeping labels-map
465   std::vector<ObjectPtr>& aVec = myObjs[ModelAPI_Feature::group()];
466   std::vector<ObjectPtr>::iterator aFIter = aVec.begin();
467   for(; aFIter != aVec.end(); aFIter++) {
468     boost::shared_ptr<Model_Data> aData = 
469       boost::dynamic_pointer_cast<Model_Data>((*aFIter)->data());
470     if (aData->label().IsEqual(theLabel))
471       return *aFIter;
472     std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = 
473       boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter)->results();
474     std::list<boost::shared_ptr<ModelAPI_Result> >::iterator aRIter = aResults.begin();
475     for(; aRIter != aResults.end(); aRIter++) {
476       boost::shared_ptr<Model_Data> aResData = 
477         boost::dynamic_pointer_cast<Model_Data>((*aRIter)->data());
478       if (aResData->label().IsEqual(theLabel))
479         return *aRIter;
480     }
481   }
482   return FeaturePtr(); // not found
483 }
484
485 boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
486 {
487   // just store sub-document identifier here to manage it later
488   if (mySubs.find(theDocID) == mySubs.end())
489     mySubs.insert(theDocID);
490   return Model_Application::getApplication()->getDocument(theDocID);
491 }
492
493 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex)
494 {
495   if (theGroupID == ModelAPI_Feature::group()) {
496     // features may be not in history but in the myObjs, so, iterate all
497     int anIndex = 0;
498     std::map<std::string, std::vector<ObjectPtr> >::iterator aFind = 
499       myObjs.find(ModelAPI_Feature::group());
500     if (aFind != myObjs.end()) {
501       std::vector<ObjectPtr>::iterator aFIter = aFind->second.begin();
502       for(; aFIter != aFind->second.end(); aFIter++) {
503         if ((*aFIter)->isInHistory()) {
504           if (theIndex == anIndex)
505             return *aFIter;
506           anIndex++;
507         }
508       }
509     }
510   } else {
511     // iterate all features in order to find the needed result
512     std::map<std::string, std::vector<ObjectPtr> >::iterator aFind = 
513       myObjs.find(ModelAPI_Feature::group());
514     if (aFind != myObjs.end()) {
515       std::vector<ObjectPtr>::iterator aFIter = aFind->second.begin();
516       for(int anIndex = 0; aFIter != aFind->second.end(); aFIter++) {
517         const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = 
518           boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter)->results();
519         std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
520         for(; aRIter != aResults.cend(); aRIter++) {
521           if ((*aRIter)->isInHistory() && (*aRIter)->groupName() == theGroupID) {
522             if (anIndex == theIndex)
523               return *aRIter;
524             anIndex++;
525           }
526         }
527       }
528     }
529   }
530   // not found
531   return ObjectPtr();
532 }
533
534 int Model_Document::size(const std::string& theGroupID) 
535 {
536   int aResult = 0;
537   if (theGroupID == ModelAPI_Feature::group()) {
538     // features may be not in history but in the myObjs, so, iterate all
539     std::map<std::string, std::vector<ObjectPtr> >::iterator aFind = 
540       myObjs.find(ModelAPI_Feature::group());
541     if (aFind != myObjs.end()) {
542       std::vector<ObjectPtr>::iterator aFIter = aFind->second.begin();
543       for(; aFIter != aFind->second.end(); aFIter++) {
544         if ((*aFIter)->isInHistory()) {
545           aResult++;
546         }
547       }
548     }
549   } else {
550     // iterate all features in order to find the needed result
551     std::map<std::string, std::vector<ObjectPtr> >::iterator aFind = 
552       myObjs.find(ModelAPI_Feature::group());
553     if (aFind != myObjs.end()) {
554       std::vector<ObjectPtr>::iterator aFIter = aFind->second.begin();
555       for(; aFIter != aFind->second.end(); aFIter++) {
556         const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = 
557           boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter)->results();
558         std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
559         for(; aRIter != aResults.cend(); aRIter++) {
560           if ((*aRIter)->isInHistory() && (*aRIter)->groupName() == theGroupID) {
561             aResult++;
562           }
563         }
564       }
565     }
566   }
567   // group is not found
568   return aResult;
569 }
570
571 Model_Document::Model_Document(const std::string theID)
572     : myID(theID), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format
573 {
574   myDoc->SetUndoLimit(UNDO_LIMIT);
575   myTransactionsAfterSave = 0;
576   myNestedNum = -1;
577   //myDoc->SetNestedTransactionMode();
578   // to have something in the document and avoid empty doc open/save problem
579   // in transaction for nesting correct working
580   myDoc->NewCommand();
581   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
582   myDoc->CommitCommand();
583 }
584
585 TDF_Label Model_Document::groupLabel(const std::string theGroup)
586 {
587   // searching for existing
588   TCollection_ExtendedString aGroup(theGroup.c_str());
589   TDF_ChildIDIterator aGroupIter(myDoc->Main().FindChild(TAG_OBJECTS), TDataStd_Comment::GetID());
590   for(; aGroupIter.More(); aGroupIter.Next()) {
591     Handle(TDataStd_Comment) aName = Handle(TDataStd_Comment)::DownCast(aGroupIter.Value());
592     if (aName->Get() == aGroup)
593       return aGroupIter.Value()->Label();
594   }
595   // create a new
596   TDF_Label aNew = myDoc->Main().FindChild(TAG_OBJECTS).NewChild();
597   TDataStd_Comment::Set(aNew, aGroup);
598   return aNew;
599 }
600
601 void Model_Document::setUniqueName(FeaturePtr theFeature)
602 {
603   std::string aName; // result
604   // first count all objects of such kind to start with index = count + 1
605   int a, aNumObjects = 0;
606   int aSize = myObjs.find(ModelAPI_Feature::group()) == myObjs.end() ? 
607     0 : myObjs[ModelAPI_Feature::group()].size();
608   for(a = 0; a < aSize; a++) {
609     if (boost::dynamic_pointer_cast<ModelAPI_Feature>(myObjs[ModelAPI_Feature::group()][a])->
610         getKind() == theFeature->getKind())
611       aNumObjects++;
612   }
613   // generate candidate name
614   std::stringstream aNameStream;
615   aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
616   aName = aNameStream.str();
617   // check this is unique, if not, increase index by 1
618   for(a = 0; a < aSize;) {
619     FeaturePtr aFeature = 
620       boost::dynamic_pointer_cast<ModelAPI_Feature>(myObjs[ModelAPI_Feature::group()][a]);
621     bool isSameName = aFeature->isInHistory() && aFeature->data()->name() == aName;
622     if (!isSameName) { // check also results to avoid same results names (actual for Parts)
623       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
624       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
625       for(; aRIter != aResults.cend(); aRIter++) {
626         isSameName = (*aRIter)->isInHistory() && (*aRIter)->data()->name() == aName;
627       }
628     }
629     if (isSameName) {
630       aNumObjects++;
631       std::stringstream aNameStream;
632       aNameStream<<theFeature->getKind()<<"_"<<aNumObjects + 1;
633       aName = aNameStream.str();
634       // reinitialize iterator to make sure a new name is unique
635       a = 0;
636     } else a++;
637   }
638   theFeature->data()->setName(aName);
639 }
640
641 void Model_Document::initData(ObjectPtr theObj, TDF_Label& theLab, const int theTag) {
642   boost::shared_ptr<ModelAPI_Document> aThis = 
643     Model_Application::getApplication()->getDocument(myID);
644   boost::shared_ptr<Model_Data> aData(new Model_Data);
645   aData->setLabel(theLab.FindChild(theTag + 1));
646   aData->setObject(theObj);
647   theObj->setDoc(aThis);
648   theObj->setData(aData);
649   FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
650   if (aFeature) aFeature->initAttributes();
651 }
652
653 void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
654 {
655   boost::shared_ptr<ModelAPI_Document> aThis = 
656     Model_Application::getApplication()->getDocument(myID);
657   // update all objects: iterate from the end: as they appeared in the list
658   std::map<std::string, std::vector<ObjectPtr> >::reverse_iterator aGroupIter = myObjs.rbegin();
659   for(; aGroupIter != myObjs.rend(); aGroupIter++) {
660     std::vector<ObjectPtr>::iterator anObjIter = aGroupIter->second.begin();
661     // and in parallel iterate labels of features
662     const std::string& aGroupName = aGroupIter->first;
663     TDF_ChildIDIterator aLabIter(groupLabel(aGroupName), TDataStd_Comment::GetID());
664     while(anObjIter != aGroupIter->second.end() || aLabIter.More()) {
665       static const int INFINITE_TAG = INT_MAX; // no label means that it exists somwhere in infinite
666       int aFeatureTag = INFINITE_TAG; 
667       if (anObjIter != aGroupIter->second.end()) { // existing tag for feature
668         boost::shared_ptr<Model_Data> aData = 
669           boost::dynamic_pointer_cast<Model_Data>((*anObjIter)->data());
670         aFeatureTag = aData->label().Tag();
671       }
672       int aDSTag = INFINITE_TAG; 
673       if (aLabIter.More()) { // next label in DS is existing
674         aDSTag = aLabIter.Value()->Label().Tag();
675       }
676       if (aDSTag > aFeatureTag) { // feature is removed
677         ObjectPtr anObj = *anObjIter;
678         anObjIter = aGroupIter->second.erase(anObjIter);
679         // event: model is updated
680         if (anObj->isInHistory()) {
681           ModelAPI_EventCreator::get()->sendDeleted(aThis, aGroupName);
682         }
683         ModelAPI_EventCreator::get()->sendDeleted(aThis, aGroupName);
684         // results of this feature must be redisplayed (hided)
685         static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
686         const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = boost::dynamic_pointer_cast<ModelAPI_Feature>(anObj)->results();
687         std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
688         for(; aRIter != aResults.cend(); aRIter++) {
689           boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
690           aRes->setData(boost::shared_ptr<ModelAPI_Data>()); // deleted flag
691           ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
692         }
693       } else if (aDSTag < aFeatureTag) { // a new feature is inserted
694         // create a feature
695         TDF_Label aLab = aLabIter.Value()->Label();
696         ObjectPtr aNewObj = ModelAPI_PluginManager::get()->createFeature(
697           TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
698           .ToCString());
699         // this must be before "setData" to redo the sketch line correctly
700         if (anObjIter == aGroupIter->second.end()) {
701           aGroupIter->second.push_back(aNewObj);
702           anObjIter = aGroupIter->second.end();
703         } else {
704           anObjIter++;
705           aGroupIter->second.insert(anObjIter, aNewObj);
706         }
707         initData(aNewObj, aLab, TAG_FEATURE_ARGUMENTS);
708
709         // event: model is updated
710         static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
711         ModelAPI_EventCreator::get()->sendUpdated(aNewObj, anEvent);
712         // feature for this label is added, so go to the next label
713         aLabIter.Next();
714       } else { // nothing is changed, both iterators are incremented
715         if (theMarkUpdated) {
716           static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
717           ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, anEvent);
718         }
719         anObjIter++;
720         aLabIter.Next();
721       }
722     }
723   }
724   // after all updates, sends a message that groups of features were created or updated
725   boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())->
726     setCheckTransactions(false);
727   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
728   if (theMarkUpdated)
729     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
730   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
731   boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())->
732     setCheckTransactions(true);
733 }
734
735 void Model_Document::storeResult(boost::shared_ptr<ModelAPI_Data> theFeatureData,
736   boost::shared_ptr<ModelAPI_Result> theResult, const int theResultIndex)
737 {
738   boost::shared_ptr<ModelAPI_Document> aThis = 
739     Model_Application::getApplication()->getDocument(myID);
740   theResult->setDoc(aThis);
741   initData(theResult, boost::dynamic_pointer_cast<Model_Data>(theFeatureData)->
742     label().Father().FindChild(TAG_FEATURE_RESULTS), theResultIndex);
743   if (theResult->data()->name().empty()) { // if was not initialized, generate event and set a name
744     static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
745     ModelAPI_EventCreator::get()->sendUpdated(theResult, anEvent);
746     theResult->data()->setName(theFeatureData->name());
747   }
748 }
749
750 boost::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
751   const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
752 {
753   boost::shared_ptr<ModelAPI_ResultConstruction> aResult(new Model_ResultConstruction());
754   storeResult(theFeatureData, aResult);
755   return aResult;
756 }
757
758 boost::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
759   const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
760 {
761   boost::shared_ptr<ModelAPI_ResultBody> aResult(new Model_ResultBody());
762   storeResult(theFeatureData, aResult);
763   return aResult;
764 }
765
766 boost::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
767   const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
768 {
769   boost::shared_ptr<ModelAPI_ResultPart> aResult(new Model_ResultPart());
770   storeResult(theFeatureData, aResult);
771   return aResult;
772 }
773
774 boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(
775   const boost::shared_ptr<ModelAPI_Result>& theResult)
776 {
777   // iterate all features in order to find the needed result
778   std::map<std::string, std::vector<ObjectPtr> >::iterator aFind = 
779     myObjs.find(ModelAPI_Feature::group());
780   if (aFind != myObjs.end()) {
781     std::vector<ObjectPtr>::iterator aFIter = aFind->second.begin();
782     for(; aFIter != aFind->second.end(); aFIter++) {
783       FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(*aFIter);
784       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
785       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
786       for(; aRIter != aResults.cend(); aRIter++) {
787         if (*aRIter == theResult) {
788           return aFeature;
789         }
790       }
791     }
792   }
793   return FeaturePtr();
794 }