]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModelHighAPI/ModelHighAPI_Dumper.cpp
Salome HOME
Python Dump: improve checking default name of a feature when there are postponed...
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include "ModelHighAPI_Dumper.h"
22
23 #include <Config_PropManager.h>
24
25 #include <GeomAPI_Pnt.h>
26 #include <GeomAPI_Dir.h>
27 #include <GeomAPI_ShapeExplorer.h>
28
29 #include <GeomDataAPI_Dir.h>
30 #include <GeomDataAPI_Point.h>
31 #include <GeomDataAPI_Point2D.h>
32
33 #include <ModelAPI_AttributeBoolean.h>
34 #include <ModelAPI_AttributeDouble.h>
35 #include <ModelAPI_AttributeIntArray.h>
36 #include <ModelAPI_AttributeInteger.h>
37 #include <ModelAPI_AttributeRefAttr.h>
38 #include <ModelAPI_AttributeRefAttrList.h>
39 #include <ModelAPI_AttributeReference.h>
40 #include <ModelAPI_AttributeRefList.h>
41 #include <ModelAPI_AttributeSelection.h>
42 #include <ModelAPI_AttributeSelectionList.h>
43 #include <ModelAPI_AttributeString.h>
44 #include <ModelAPI_AttributeStringArray.h>
45 #include <ModelAPI_CompositeFeature.h>
46 #include <ModelAPI_Document.h>
47 #include <ModelAPI_Entity.h>
48 #include <ModelAPI_Feature.h>
49 #include <ModelAPI_Folder.h>
50 #include <ModelAPI_Result.h>
51 #include <ModelAPI_ResultBody.h>
52 #include <ModelAPI_ResultConstruction.h>
53 #include <ModelAPI_ResultPart.h>
54 #include <ModelAPI_Session.h>
55 #include <ModelAPI_Tools.h>
56
57 #include <ModelGeomAlgo_Shape.h>
58
59 #include <PartSetPlugin_Part.h>
60
61 #include <OSD_OpenFile.hxx>
62
63 #include <fstream>
64
65 static int gCompositeStackDepth = 0;
66
67 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
68
69 ModelHighAPI_Dumper::ModelHighAPI_Dumper()
70   : myGeometricalSelection(false)
71 {
72   clear();
73 }
74
75 void ModelHighAPI_Dumper::setInstance(ModelHighAPI_Dumper* theDumper)
76 {
77   if (mySelf == 0)
78     mySelf = theDumper;
79 }
80
81 ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
82 {
83   return mySelf;
84 }
85
86 void ModelHighAPI_Dumper::clear(bool bufferOnly)
87 {
88   myDumpBuffer.str("");
89   myDumpBuffer << std::setprecision(16);
90
91   clearNotDumped();
92
93   if (!bufferOnly) {
94     myFullDump.str("");
95     myFullDump << std::setprecision(16);
96
97     myNames.clear();
98     myModules.clear();
99     myFeatureCount.clear();
100     while (!myEntitiesStack.empty())
101       myEntitiesStack.pop();
102
103     myPostponed.clear();
104     myDumpPostponedInProgress = false;
105   }
106 }
107
108 void ModelHighAPI_Dumper::clearNotDumped()
109 {
110   myNotDumpedEntities.clear();
111 }
112
113 // Convert string to integer. If the string is not a number, return -1
114 static int toInt(const std::string& theString)
115 {
116   std::string::const_iterator aChar = theString.begin();
117   for (; aChar != theString.end(); ++aChar)
118     if (!std::isdigit(*aChar))
119       break;
120   if (aChar != theString.end())
121     return -1; // not a number
122   return std::stoi(theString);
123 }
124
125 const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
126                                              bool theSaveNotDumped,
127                                              bool theUseEntityName)
128 {
129   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
130   if (aFound != myNames.end())
131     return aFound->second.myCurrentName;
132
133   // entity is not found, store it
134   std::string aName, aKind;
135   bool isDefaultName = false;
136   bool isSaveNotDumped = theSaveNotDumped;
137   std::ostringstream aDefaultName;
138   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
139   if (aFeature) {
140     aName = aFeature->name();
141     aKind = aFeature->getKind();
142   } else {
143     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(theEntity);
144     if (aFolder) {
145       aName = aFolder->data()->name();
146       aKind = ModelAPI_Folder::ID();
147       isSaveNotDumped = false;
148     }
149   }
150
151   ObjectPtr anObject = std::dynamic_pointer_cast<ModelAPI_Object>(theEntity);
152   if (anObject) {
153     DocumentPtr aDoc = anObject->document();
154     std::pair<int, int>& aNbFeatures = myFeatureCount[aDoc][aKind];
155     aNbFeatures.first += 1;
156
157     size_t anIndex = aName.find(aKind);
158     if (anIndex == 0 && aName[aKind.length()] == '_') { // name starts with "FeatureKind_"
159       std::string anIdStr = aName.substr(aKind.length() + 1);
160       int anId = toInt(anIdStr);
161
162       // Check number of already registered objects of such kind. Index of current object
163       // should be the same to identify feature's name as automatically generated.
164       if (aNbFeatures.first == anId && aNbFeatures.second < anId) {
165         // name is not user-defined
166         isDefaultName = true;
167
168         // check there are postponed features of this kind,
169         // dump their names, because the sequence of features may be changed
170         for (std::list<EntityPtr>::const_iterator aPpIt = myPostponed.begin();
171             aPpIt != myPostponed.end(); ++aPpIt) {
172           FeaturePtr aCurFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aPpIt);
173           if (aCurFeature && aCurFeature->getKind() == aKind) {
174             myNames[*aPpIt].myIsDefault = false;
175             isDefaultName = false;
176           }
177         }
178       }
179
180       if (anId > aNbFeatures.second)
181         aNbFeatures.second = anId;
182     }
183
184     // obtain default name for the feature
185     if (theUseEntityName)
186       aDefaultName << aName;
187     else {
188       int aFullIndex = 0;
189       NbFeaturesMap::const_iterator aFIt = myFeatureCount.begin();
190       for (; aFIt != myFeatureCount.end(); ++aFIt) {
191         std::map<std::string, std::pair<int, int> >::const_iterator aFound =
192           aFIt->second.find(aKind);
193         if (aFound != aFIt->second.end())
194           aFullIndex += aFound->second.first;
195       }
196       aDefaultName << aKind << "_" << aFullIndex;
197     }
198   }
199
200   myNames[theEntity] = EntityName(aDefaultName.str(), aName, isDefaultName);
201   if (isSaveNotDumped)
202     myNotDumpedEntities.insert(theEntity);
203
204   // store names of results
205   if (aFeature)
206     saveResultNames(aFeature);
207
208   return myNames[theEntity].myCurrentName;
209 }
210
211 const std::string& ModelHighAPI_Dumper::parentName(const FeaturePtr& theEntity)
212 {
213   const std::set<AttributePtr>& aRefs = theEntity->data()->refsToMe();
214   std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
215   for (; aRefIt != aRefs.end(); ++aRefIt) {
216     CompositeFeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(
217         ModelAPI_Feature::feature((*aRefIt)->owner()));
218     if (anOwner)
219       return name(anOwner);
220   }
221
222   static const std::string DUMMY;
223   return DUMMY;
224 }
225
226 void ModelHighAPI_Dumper::saveResultNames(const FeaturePtr& theFeature)
227 {
228   // Default name of the feature
229   bool isFeatureDefaultName = myNames[theFeature].myIsDefault;
230
231   // Save only names of results which is not correspond to default feature name
232   const std::list<ResultPtr>& aResults = theFeature->results();
233   std::list<ResultPtr> allRes;
234   ModelAPI_Tools::allResults(theFeature, allRes);
235   for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
236     std::pair<std::string, bool> aName = ModelAPI_Tools::getDefaultName(*aRes);
237     std::string aDefaultName = aName.first;
238     std::string aResName = (*aRes)->data()->name();
239     bool isUserDefined = !(isFeatureDefaultName && aDefaultName == aResName);
240     myNames[*aRes] =
241       EntityName(aResName, (isUserDefined ? aResName : std::string()), !isUserDefined);
242   }
243 }
244
245 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc,
246                                   const std::string& theFileName)
247 {
248   // dump top level document feature
249   static const std::string aDocName("partSet");
250   myNames[theDoc] = EntityName(aDocName, std::string(), true);
251   *this << aDocName << " = model.moduleDocument()" << std::endl;
252
253   // dump subfeatures and store result to file
254   return process(theDoc) && exportTo(theFileName);
255 }
256
257 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
258 {
259   bool isOk = true;
260   std::list<ObjectPtr> anObjects = theDoc->allObjects();
261   std::list<ObjectPtr>::const_iterator anObjIt = anObjects.begin();
262   // firstly, dump all parameters
263   for (; anObjIt != anObjects.end(); ++ anObjIt) {
264     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
265     if (aFeature)
266       dumpParameter(aFeature);
267   }
268   // dump all other features
269   for (anObjIt = anObjects.begin(); anObjIt != anObjects.end(); ++anObjIt) {
270     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anObjIt);
271     if (aCompFeat) // iteratively process composite features
272       isOk = process(aCompFeat) && isOk;
273     else if (!isDumped(EntityPtr(*anObjIt))) {
274       // dump folder
275       FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anObjIt);
276       if (aFolder)
277         dumpFolder(aFolder);
278       else {
279         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
280         if (aFeature) // dump common feature
281           dumpFeature(aFeature);
282       }
283     }
284   }
285   // dump folders if any
286   dumpPostponed(true);
287   return isOk;
288 }
289
290 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
291                                   bool isForce)
292 {
293   // increase composite features stack
294   ++gCompositeStackDepth;
295   // dump composite itself
296   if (!isDumped(EntityPtr(theComposite)) || isForce)
297     dumpFeature(FeaturePtr(theComposite), isForce);
298
299   // sub-part is processed independently, because it provides separate document
300   if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
301     // dump name of the part if it is different from default
302     if (!myEntitiesStack.empty())
303       dumpEntitySetName();
304
305     // decrease composite features stack because we run into separate document
306     --gCompositeStackDepth;
307
308     ResultPartPtr aPartResult =
309         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
310     if (!aPartResult)
311       return false;
312     DocumentPtr aSubDoc = aPartResult->partDoc();
313     if (!aSubDoc)
314       return false;
315     // set name of document
316     const std::string& aPartName = myNames[theComposite].myCurrentName;
317     std::string aDocName = aPartName + "_doc";
318     myNames[aSubDoc] = EntityName(aDocName, std::string(), true);
319
320     // dump document in a separate line
321     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
322     // dump features in the document
323     bool aRes = process(aSubDoc);
324     *this << "model.do()" << std::endl;
325     return aRes;
326   }
327
328   // dump sub-features
329   bool isOk = processSubs(theComposite);
330   // decrease composite features stack
331   --gCompositeStackDepth;
332
333   return isOk;
334 }
335
336 bool ModelHighAPI_Dumper::processSubs(
337   const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
338   bool theDumpModelDo)
339 {
340   bool isOk = true;
341   // dump all sub-features;
342   bool isSubDumped = false;
343   int aNbSubs = theComposite->numberOfSubs();
344   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
345     FeaturePtr aFeature = theComposite->subFeature(anIndex);
346     if (isDumped(EntityPtr(aFeature)))
347       continue;
348
349     isSubDumped = true;
350     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
351     if (aCompFeat) // iteratively process composite features
352       isOk = process(aCompFeat) && isOk;
353     else
354       dumpFeature(aFeature, true);
355   }
356
357   bool isDumpSetName = !myEntitiesStack.empty() &&
358       myEntitiesStack.top().myEntity == EntityPtr(theComposite);
359   bool isForceModelDo = isSubDumped && isDumpSetName &&
360       (myEntitiesStack.top().myUserName || !myEntitiesStack.top().myResults.empty());
361   // It is necessary for the sketch to create its result when complete (command "model.do()").
362   // This option is set by flat theDumpModelDo.
363   // However, nested sketches are rebuilt by parent feature, so, they do not need
364   // explicit call of "model.do()". This will be controlled by the depth of the stack.
365   if (isForceModelDo || (theDumpModelDo && gCompositeStackDepth <= 1))
366     *this << "model.do()" << std::endl;
367
368   // dump "setName" for composite feature
369   if (isDumpSetName)
370     dumpEntitySetName();
371   return isOk;
372 }
373
374 void ModelHighAPI_Dumper::postpone(const EntityPtr& theEntity)
375 {
376   // keep the name
377   name(theEntity, false);
378   myPostponed.push_back(theEntity);
379 }
380
381 void ModelHighAPI_Dumper::dumpPostponed(bool theDumpFolders)
382 {
383   if (myDumpPostponedInProgress)
384     return;
385
386   myDumpPostponedInProgress = true;
387   // make a copy of postponed entities, because the list will be updated
388   // if some features are not able to be dumped
389   std::list<EntityPtr> aPostponedCopy = myPostponed;
390   myPostponed.clear();
391
392   // iterate over postponed entities and try to dump them
393   std::list<EntityPtr>::const_iterator anIt = aPostponedCopy.begin();
394   for (; anIt != aPostponedCopy.end(); ++anIt) {
395     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anIt);
396     if (aFolder) {
397       if (theDumpFolders)
398         dumpFolder(aFolder);
399       else
400         myPostponed.push_back(*anIt);
401     }
402     else {
403       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
404       if (aFeature)
405         dumpFeature(aFeature, true);
406     }
407   }
408   myDumpPostponedInProgress = false;
409 }
410
411 void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFeatureGet,
412                                                      const FeaturePtr& theSubFeature)
413 {
414   name(theSubFeature, false);
415   myNames[theSubFeature] = EntityName(theSubFeatureGet, theSubFeature->name(), false);
416
417   // store results if they have user-defined names or colors
418   std::list<ResultPtr> aResultsWithNameOrColor;
419   const std::list<ResultPtr>& aResults = theSubFeature->results();
420   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
421   for (; aResIt != aResults.end(); ++aResIt) {
422     std::string aResName = (*aResIt)->data()->name();
423     myNames[*aResIt] = EntityName(aResName, aResName, false);
424     aResultsWithNameOrColor.push_back(*aResIt);
425   }
426
427   // store just dumped entity to stack
428   myEntitiesStack.push(LastDumpedEntity(theSubFeature, true, aResultsWithNameOrColor));
429
430   dumpEntitySetName();
431 }
432
433 bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
434 {
435   std::ofstream aFile;
436   OSD_OpenStream(aFile, theFileName.c_str(), std::ofstream::out);
437   if (!aFile.is_open())
438     return false;
439
440   // standard header (encoding + imported modules)
441   aFile << "# -*- coding: utf-8 -*-" << std::endl << std::endl;
442   for (ModulesMap::const_iterator aModIt = myModules.begin();
443        aModIt != myModules.end(); ++aModIt) {
444     aFile << "from " << aModIt->first << " import ";
445     if (aModIt->second.empty() ||
446         aModIt->second.find(std::string()) != aModIt->second.end())
447       aFile << "*"; // import whole module
448     else {
449       // import specific features
450       std::set<std::string>::const_iterator anObjIt = aModIt->second.begin();
451       aFile << *anObjIt;
452       for (++anObjIt; anObjIt != aModIt->second.end(); ++anObjIt)
453         aFile << ", " << *anObjIt;
454     }
455     aFile << std::endl;
456   }
457   if (!myModules.empty())
458     aFile << std::endl;
459
460   aFile << "from salome.shaper import model" << std::endl << std::endl;
461   aFile << "model.begin()" << std::endl;
462
463   // dump collected data
464   aFile << myFullDump.str();
465   aFile << myDumpBuffer.str();
466
467   // standard footer
468   aFile << "model.end()" << std::endl;
469
470   aFile.close();
471   clear();
472
473   return true;
474 }
475
476 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName,
477                                        const std::string& theObject)
478 {
479   myModules[theModuleName].insert(theObject);
480 }
481
482 void ModelHighAPI_Dumper::dumpEntitySetName()
483 {
484   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
485   bool isBufferEmpty = myDumpBuffer.str().empty();
486
487   // dump "setName" for the entity
488   if (aLastDumped.myUserName) {
489     EntityName& anEntityNames = myNames[aLastDumped.myEntity];
490     if (!anEntityNames.myIsDefault)
491       myDumpBuffer << anEntityNames.myCurrentName << ".setName(\""
492                    << anEntityNames.myUserName << "\")" << std::endl;
493     // don't dump "setName" for the entity twice
494     anEntityNames.myUserName.clear();
495     anEntityNames.myIsDefault = true;
496   }
497   // dump "setName" for results
498   std::list<ResultPtr>::const_iterator aResIt = aLastDumped.myResults.begin();
499   std::list<ResultPtr>::const_iterator aResEnd = aLastDumped.myResults.end();
500   for (; aResIt != aResEnd; ++aResIt) {
501     // set result name
502     EntityName& anEntityNames = myNames[*aResIt];
503     if (!anEntityNames.myIsDefault) {
504       *this << *aResIt;
505       myDumpBuffer << ".setName(\"" << anEntityNames.myUserName << "\")" << std::endl;
506       // don't dump "setName" for the entity twice
507       anEntityNames.myUserName.clear();
508       anEntityNames.myIsDefault = true;
509     }
510     // set result color
511     if (!isDefaultColor(*aResIt)) {
512       AttributeIntArrayPtr aColor = (*aResIt)->data()->intArray(ModelAPI_Result::COLOR_ID());
513       if (aColor && aColor->isInitialized()) {
514         *this << *aResIt;
515         myDumpBuffer << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
516                      << ", " << aColor->value(2) << ")" << std::endl;
517       }
518     }
519     // set result deflection
520     if (!isDefaultDeflection(*aResIt)) {
521       AttributeDoublePtr aDeflectionAttr =
522         (*aResIt)->data()->real(ModelAPI_Result::DEFLECTION_ID());
523       if(aDeflectionAttr.get() && aDeflectionAttr->isInitialized()) {
524         *this << *aResIt;
525         myDumpBuffer << ".setDeflection(" << aDeflectionAttr->value() << ")" << std::endl;
526       }
527     }
528     // set result transparency
529     if (!isDefaultTransparency(*aResIt)) {
530       AttributeDoublePtr aTransparencyAttr =
531         (*aResIt)->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
532       if(aTransparencyAttr.get() && aTransparencyAttr->isInitialized()) {
533         *this << *aResIt;
534         myDumpBuffer << ".setTransparency(" << aTransparencyAttr->value() << ")" << std::endl;
535       }
536     }
537   }
538
539   myNames[aLastDumped.myEntity].myIsDumped = true;
540   myEntitiesStack.pop();
541
542   // clean buffer if it was clear before
543   if (isBufferEmpty) {
544     myFullDump << myDumpBuffer.str();
545     myDumpBuffer.str("");
546   }
547 }
548
549 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
550 {
551   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
552   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
553   return (aFound != myNames.end() && aFound->second.myIsDumped) ||
554          myFeaturesToSkip.find(aFeature) != myFeaturesToSkip.end();
555 }
556
557 bool ModelHighAPI_Dumper::isDumped(const AttributeRefAttrPtr& theRefAttr) const
558 {
559   FeaturePtr aFeature;
560   if (theRefAttr->isObject())
561     aFeature = ModelAPI_Feature::feature(theRefAttr->object());
562   else
563     aFeature = ModelAPI_Feature::feature(theRefAttr->attr()->owner());
564   return aFeature && isDumped(EntityPtr(aFeature));
565 }
566
567 bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
568 {
569   std::list<ObjectPtr> aRefs = theRefList->list();
570   std::list<ObjectPtr>::iterator anIt = aRefs.begin();
571   for (; anIt != aRefs.end(); ++anIt) {
572     FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
573     if (aFeature && !isDumped(EntityPtr(aFeature)))
574       return false;
575   }
576   return true;
577 }
578
579 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
580 {
581   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
582   if (!aColor || !aColor->isInitialized())
583     return true;
584
585   std::string aSection, aName, aDefault;
586   theResult->colorConfigInfo(aSection, aName, aDefault);
587
588   // dump current color
589   std::ostringstream aColorInfo;
590   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
591
592   return aDefault == aColorInfo.str();
593 }
594
595 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
596 {
597   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
598   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
599     return true;
600   }
601
602   double aCurrent = aDeflectionAttr->value();
603   double aDefault = -1;
604
605   bool isConstruction = false;
606   std::string aResultGroup = theResult->groupName();
607   if (aResultGroup == ModelAPI_ResultConstruction::group())
608     isConstruction = true;
609   else if (aResultGroup == ModelAPI_ResultBody::group()) {
610     GeomShapePtr aGeomShape = theResult->shape();
611     if (aGeomShape.get()) {
612       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
613       // correction of deviation for them should not influence to the application performance
614       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
615       isConstruction = !anExp.more();
616     }
617   }
618   if (isConstruction)
619     aDefault = Config_PropManager::real("Visualization", "construction_deflection");
620   else
621     aDefault = Config_PropManager::real("Visualization", "body_deflection");
622
623   return fabs(aCurrent - aDefault) < 1.e-12;
624 }
625
626 bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) const
627 {
628   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
629   if(!anAttribute || !anAttribute->isInitialized()) {
630     return true;
631   }
632   return fabs(anAttribute->value()) < 1.e-12;
633 }
634
635 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
636 {
637   myDumpBuffer << theChar;
638   return *this;
639 }
640
641 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
642 {
643   myDumpBuffer << theString;
644   return *this;
645 }
646
647 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
648 {
649   myDumpBuffer << theString;
650   return *this;
651 }
652
653 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
654 {
655   myDumpBuffer << (theValue ? "True" : "False");
656   return *this;
657 }
658
659 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
660 {
661   myDumpBuffer << theValue;
662   return *this;
663 }
664
665 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
666 {
667   myDumpBuffer << theValue;
668   return *this;
669 }
670
671 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
672 {
673   importModule("GeomAPI", "GeomAPI_Pnt");
674   myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
675                << thePoint->y() << ", " << thePoint->z() << ")";
676   return *this;
677 }
678
679 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
680 {
681   importModule("GeomAPI", "GeomAPI_Dir");
682   myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
683                << theDir->y() << ", " << theDir->z() << ")";
684   return *this;
685 }
686
687 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
688     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
689 {
690   myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
691   return *this;
692 }
693
694 static void dumpArray(std::ostringstream& theOutput, int theSize,
695                       double* theValues, std::string* theTexts)
696 {
697   for (int i = 0; i < theSize; ++i) {
698     if (i > 0)
699       theOutput << ", ";
700     if (theTexts[i].empty())
701       theOutput << theValues[i];
702     else
703       theOutput << "\"" << theTexts[i] << "\"";
704   }
705 }
706
707 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
708     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
709 {
710   static const int aSize = 3;
711   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
712   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
713   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
714   return *this;
715 }
716
717 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
718     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
719 {
720   static const int aSize = 2;
721   double aValues[aSize] = {thePoint->x(), thePoint->y()};
722   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
723   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
724   return *this;
725 }
726
727 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
728     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
729 {
730   myDumpBuffer << (theAttrBool->value() ? "True" : "False");
731   return *this;
732 }
733
734 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
735     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
736 {
737   std::string aText = theAttrInt->text();
738   if (aText.empty())
739     myDumpBuffer << theAttrInt->value();
740   else
741     myDumpBuffer << "\"" << aText << "\"";
742   return *this;
743 }
744
745 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
746     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
747 {
748   std::string aText = theAttrReal->text();
749   if (aText.empty())
750     myDumpBuffer << theAttrReal->value();
751   else
752     myDumpBuffer << "\"" << aText << "\"";
753   return *this;
754 }
755
756 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
757     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
758 {
759   myDumpBuffer << "\"" << theAttrStr->value() << "\"";
760   return *this;
761 }
762
763 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
764 {
765   myDumpBuffer << name(theFolder);
766
767   // add dumped folder to a stack
768   if (!myNames[theFolder].myIsDumped &&
769      (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theFolder))
770     myEntitiesStack.push(LastDumpedEntity(theFolder, !myNames[theFolder].myIsDefault));
771
772   return *this;
773 }
774
775 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
776 {
777   myDumpBuffer << name(theEntity);
778
779   if (!myNames[theEntity].myIsDumped) {
780     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
781     // store results if they have user-defined names or colors
782     std::list<ResultPtr> aResultsWithNameOrColor;
783     std::list<ResultPtr> allRes;
784     ModelAPI_Tools::allResults(theEntity, allRes);
785     for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
786       if(!myNames[*aRes].myIsDefault || !isDefaultColor(*aRes) ||
787          !isDefaultDeflection(*aRes) || !isDefaultTransparency(*aRes))
788         aResultsWithNameOrColor.push_back(*aRes);
789     }
790     // store just dumped entity to stack
791     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
792       myEntitiesStack.push(
793           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
794   }
795
796   // remove entity from the list of not dumped items
797   myNotDumpedEntities.erase(theEntity);
798   return *this;
799 }
800
801 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
802 {
803   // iterate in the structure of sub-results to the parent
804   ResultPtr aCurRes = theResult;
805   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
806   std::list<int> anIndices; // indexes of results in the parent result, starting from topmost
807   while(aCurRes.get()) {
808     ResultBodyPtr aParent = ModelAPI_Tools::bodyOwner(aCurRes);
809     if (aParent) {
810       anIndices.push_front(ModelAPI_Tools::bodyIndex(aCurRes));
811     } else { // index of the result in the feature
812       std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
813       for(int anIndex = 0; aRes != aFeature->results().cend(); aRes++, anIndex++) {
814         if (*aRes == aCurRes) {
815           anIndices.push_front(anIndex);
816           break;
817         }
818       }
819     }
820     aCurRes = aParent;
821   }
822
823   myDumpBuffer << name(aFeature);
824   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
825     if (anI == anIndices.begin()) {
826       if(*anI == 0) {
827         myDumpBuffer << ".result()";
828       }
829       else {
830         myDumpBuffer << ".results()[" << *anI << "]";
831       }
832     } else {
833       myDumpBuffer << ".subResult(" << *anI << ")";
834     }
835   }
836
837   return *this;
838 }
839
840 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
841 {
842   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
843   if(aFeature.get()) {
844     myDumpBuffer << name(aFeature);
845     return *this;
846   }
847
848   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
849   if(aResult.get()) {
850     *this << aResult;
851     return *this;
852   }
853
854   return *this;
855 }
856
857 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
858 {
859   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
860
861   std::string aWrapperPrefix, aWrapperSuffix;
862   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
863   // In this case we need to cast explicitly feature to appropriate type.
864   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
865   if (isCopy.get() && isCopy->value()) {
866     aWrapperPrefix = featureWrapper(anOwner) + "(";
867     aWrapperSuffix = ")";
868     importModule("SketchAPI");
869   }
870
871   myDumpBuffer << aWrapperPrefix << name(anOwner) << aWrapperSuffix
872                << "." << attributeGetter(anOwner, theAttr->id()) << "()";
873   return *this;
874 }
875
876 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
877     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
878 {
879   if (theRefAttr->isObject())
880     *this << theRefAttr->object();
881   else
882     *this << theRefAttr->attr();
883   return *this;
884 }
885
886 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
887     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
888 {
889   myDumpBuffer << "[";
890   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
891   bool isAdded = false;
892   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
893   for (; anIt != aList.end(); ++anIt) {
894     if (isAdded)
895       myDumpBuffer << ", ";
896     else
897       isAdded = true;
898     if (anIt->first)
899       *this << anIt->first;
900     else if (anIt->second)
901       * this << anIt->second;
902   }
903   myDumpBuffer << "]";
904   return *this;
905 }
906
907 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
908     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
909 {
910   *this << theReference->value();
911   return *this;
912 }
913
914 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
915     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
916 {
917   static const int aThreshold = 2;
918   // if number of elements in the list if greater than a threshold,
919   // dump it in a separate line with specific name
920   std::string aDumped = myDumpBuffer.str();
921   if (aDumped.empty() || theRefList->size() <= aThreshold) {
922     myDumpBuffer << "[";
923     std::list<ObjectPtr> aList = theRefList->list();
924     bool isAdded = false;
925     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
926     for (; anIt != aList.end(); ++anIt) {
927       if (isAdded)
928         myDumpBuffer << ", ";
929       else
930         isAdded = true;
931
932       *this << *anIt;
933     }
934     myDumpBuffer << "]";
935   } else {
936     // clear buffer and store list "as is"
937     myDumpBuffer.str("");
938     *this << theRefList;
939     // save buffer and clear it again
940     std::string aDumpedList = myDumpBuffer.str();
941     myDumpBuffer.str("");
942     // obtain name of list
943     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
944     std::string aListName = name(anOwner) + "_objects";
945     // store all previous data
946     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
947                  << aDumped << aListName;
948   }
949   return *this;
950 }
951
952 static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
953                                      const ResultPtr& theResult,
954                                      const GeomShapePtr& theShape,
955                                      const FeaturePtr& theStartFeature,
956                                      const FeaturePtr& theEndFeature)
957 {
958   DocumentPtr aDoc1 = theStartFeature->document();
959   DocumentPtr aDoc2 = theEndFeature->document();
960
961   std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
962   if (aDoc1 != aDoc2) {
963     std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
964     aFeatures.insert(aFeatures.end(), anAdditionalFeatures.begin(), anAdditionalFeatures.end());
965   }
966
967   CompositeFeaturePtr aLastCompositeFeature;
968
969   std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
970   while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
971     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
972     if (aCompFeat)
973       aLastCompositeFeature = aCompFeat;
974     ++aFIt;
975   }
976
977   // collect the list of composite features, containing the last feature;
978   // these features should be excluded from searching,
979   // because the feature cannot select sub-shapes from its parent
980   std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
981
982   int aNbPossibleSelections = 0;
983   for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
984     bool isSkipFeature = false;
985     if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
986       isSkipFeature = true;
987     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
988     if (aCompFeat) {
989       ResultPartPtr aPartRes =
990           std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
991       if (!aPartRes)
992         aLastCompositeFeature = aCompFeat;
993       if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
994         // do not process the parent for the last feature,
995         // because it cannot select objects from its parent
996         isSkipFeature = true;
997       }
998     }
999     if (isSkipFeature)
1000       continue;
1001
1002     std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
1003     if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
1004                                                  anApproproate)) {
1005       std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
1006       for (; anApIt != anApproproate.end(); ++anApIt) {
1007         ++aNbPossibleSelections;
1008
1009         // stop if the target shape and result are found
1010         GeomShapePtr aCurShape = anApIt->mySubshape;
1011         if (!aCurShape)
1012           aCurShape = anApIt->myResult->shape();
1013
1014         if (anApIt->myResult->isSame(theResult) && aCurShape->isSame(theShape))
1015           break;
1016       }
1017     }
1018   }
1019   return aNbPossibleSelections;
1020 }
1021
1022 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1023     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
1024 {
1025   myDumpBuffer << "model.selection(";
1026
1027   if(!theAttrSelect->isInitialized()) {
1028     myDumpBuffer << ")";
1029     return *this;
1030   }
1031
1032   GeomShapePtr aShape = theAttrSelect->value();
1033   if(!aShape.get()) {
1034     aShape = theAttrSelect->context()->shape();
1035   }
1036
1037   if(!aShape.get()) {
1038     myDumpBuffer << ")";
1039     return *this;
1040   }
1041
1042   // how to dump selection: construction features are dumped by name always
1043   bool isDumpByGeom = myGeometricalSelection;
1044   FeaturePtr aSelectedFeature;
1045   if (isDumpByGeom) {
1046     ResultPtr aRes = theAttrSelect->context();
1047     FeaturePtr aFeature = theAttrSelect->contextFeature();
1048     if (aRes && !aFeature)
1049       aSelectedFeature = ModelAPI_Feature::feature(aRes->data()->owner());
1050     isDumpByGeom = aSelectedFeature && aSelectedFeature->isInHistory();
1051   }
1052
1053   myDumpBuffer << "\"" << aShape->shapeTypeStr();
1054   if (isDumpByGeom) {
1055     // check the selected item is a ResultPart;
1056     // in this case it is necessary to get shape with full transformation
1057     // for correct calculation of the middle point
1058     ResultPartPtr aResPart =
1059         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
1060     if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
1061       aShape = aResPart->shape();
1062     GeomPointPtr aMiddlePoint = aShape->middlePoint();
1063     // calculate number of features, which could be selected by the same point
1064     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
1065     int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
1066         theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
1067
1068     // produce the index if the number of applicable features is greater than 1
1069     std::string anIndex;
1070     if (aNbPossibleSelections > 1) {
1071       std::ostringstream anOutput;
1072       anOutput << "_" << aNbPossibleSelections;
1073       anIndex = anOutput.str();
1074     }
1075
1076     myDumpBuffer << anIndex << "\", ";
1077     *this << aMiddlePoint;
1078   }
1079   else
1080     myDumpBuffer << "\", \"" << theAttrSelect->namingName() << "\"";
1081   myDumpBuffer << ")";
1082   return *this;
1083 }
1084
1085 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1086     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
1087 {
1088   static const int aThreshold = 2;
1089   // if number of elements in the list if greater than a threshold,
1090   // dump it in a separate line with specific name
1091   std::string aDumped = myDumpBuffer.str();
1092
1093   if (aDumped.empty() || theAttrSelList->size() <= aThreshold) {
1094     myDumpBuffer << "[";
1095
1096     GeomShapePtr aShape;
1097     std::string aShapeTypeStr;
1098
1099     bool isAdded = false;
1100
1101     for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
1102       AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
1103       aShape = anAttribute->value();
1104       if(!aShape.get()) {
1105         ResultPtr aContext = anAttribute->context();
1106         if (aContext.get())
1107           aShape = aContext->shape();
1108       }
1109
1110       if(!aShape.get()) {
1111         continue;
1112       }
1113
1114       if(isAdded) {
1115         myDumpBuffer << ", ";
1116       } else {
1117         isAdded = true;
1118       }
1119       *this << anAttribute;
1120     }
1121
1122     myDumpBuffer << "]";
1123   } else {
1124     // clear buffer and store list "as is"
1125     myDumpBuffer.str("");
1126     *this << theAttrSelList;
1127     // save buffer and clear it again
1128     std::string aDumpedList = myDumpBuffer.str();
1129     myDumpBuffer.str("");
1130     // obtain name of list (the feature may contain several selection lists)
1131     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
1132     std::string aListName = name(anOwner) + "_objects";
1133     std::list<AttributePtr> aSelLists =
1134         anOwner->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
1135     if (aSelLists.size() > 1) {
1136       int anIndex = 1;
1137       for (std::list<AttributePtr>::iterator aSIt = aSelLists.begin();
1138            aSIt != aSelLists.end(); ++aSIt, ++anIndex)
1139         if ((*aSIt).get() == theAttrSelList.get())
1140           break;
1141       std::ostringstream aSStream;
1142       aSStream << aListName << "_" << anIndex;
1143       aListName = aSStream.str();
1144     }
1145     // store all previous data
1146     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
1147                  << aDumped << aListName;
1148   }
1149   return *this;
1150 }
1151
1152 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1153   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
1154 {
1155   myDumpBuffer<<"[";
1156   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
1157     if (anIndex != 0)
1158       myDumpBuffer<<", ";
1159
1160     myDumpBuffer<<"\""<<theArray->value(anIndex)<<"\"";
1161   }
1162
1163   myDumpBuffer<<"]";
1164   return *this;
1165 }
1166
1167 /// Dump std::endl
1168 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
1169                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
1170 {
1171   theDumper.myDumpBuffer << theEndl;
1172
1173   if (!theDumper.myEntitiesStack.empty()) {
1174     bool isCopy;
1175     // all copies have been stored into stack, pop them all
1176     do {
1177       isCopy = false;
1178       // Name for composite feature is dumped when all sub-entities are dumped
1179       // (see method ModelHighAPI_Dumper::processSubs).
1180       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
1181       CompositeFeaturePtr aComposite =
1182           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
1183       if (!aComposite) {
1184         theDumper.dumpEntitySetName();
1185         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
1186         if (aFeature) {
1187           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1188           isCopy = aCopyAttr.get() && aCopyAttr->value();
1189         }
1190       }
1191     } while (isCopy && !theDumper.myEntitiesStack.empty());
1192   }
1193
1194   // store all not-dumped entities first
1195   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
1196   std::string aBufCopy = theDumper.myDumpBuffer.str();
1197   theDumper.clear(true);
1198   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
1199   for (; anIt != aNotDumped.end(); ++anIt) {
1200     // if the feature is composite, dump it with all subs
1201     CompositeFeaturePtr aCompFeat =
1202         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
1203     if (aCompFeat)
1204       theDumper.process(aCompFeat, true);
1205     else {
1206       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1207       theDumper.dumpFeature(aFeature, true);
1208       // dump the Projection feature which produces this "Copy" entity
1209       AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1210       if (aCopyAttr.get() && aCopyAttr->value())
1211       {
1212         const std::set<AttributePtr>& aRefs = aFeature->data()->refsToMe();
1213         std::set<AttributePtr>::iterator aRefIt = aRefs.begin();
1214         for (; aRefIt != aRefs.end(); ++aRefIt)
1215           if ((*aRefIt)->id() == "ProjectedFeature")
1216           { // process projection only
1217             FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
1218             if (anOwner && !theDumper.isDumped(EntityPtr(anOwner)))
1219               theDumper.dumpFeature(anOwner, true);
1220           }
1221       }
1222     }
1223   }
1224
1225   // avoid multiple empty lines
1226   size_t anInd = std::string::npos;
1227   while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
1228     aBufCopy.erase(anInd, 1);
1229   // then store currently dumped string
1230   theDumper.myFullDump << aBufCopy;
1231
1232   // now, store all postponed features
1233   theDumper.dumpPostponed();
1234
1235   return theDumper;
1236 }