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