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