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