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