]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModelHighAPI/ModelHighAPI_Dumper.cpp
Salome HOME
Issue #2635: Dump to Python generates script with unnecessary setColor() calls.
[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 static bool isSketchSub(const FeaturePtr& theFeature)
580 {
581   static const std::string SKETCH("Sketch");
582   CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theFeature);
583   return anOwner && anOwner->getKind() == SKETCH;
584 }
585
586 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
587 {
588   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
589   if (!aColor || !aColor->isInitialized())
590     return true;
591
592   // check the result belongs to sketch entity, do not dump color in this way
593   ResultConstructionPtr aResConstr =
594       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theResult);
595   if (aResConstr) {
596     FeaturePtr aFeature = ModelAPI_Feature::feature(theResult->data()->owner());
597     if (isSketchSub(aFeature))
598       return true;
599   }
600
601   std::string aSection, aName, aDefault;
602   theResult->colorConfigInfo(aSection, aName, aDefault);
603
604   // dump current color
605   std::ostringstream aColorInfo;
606   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
607
608   return aDefault == aColorInfo.str();
609 }
610
611 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
612 {
613   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
614   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
615     return true;
616   }
617
618   double aCurrent = aDeflectionAttr->value();
619   double aDefault = -1;
620
621   bool isConstruction = false;
622   std::string aResultGroup = theResult->groupName();
623   if (aResultGroup == ModelAPI_ResultConstruction::group())
624     isConstruction = true;
625   else if (aResultGroup == ModelAPI_ResultBody::group()) {
626     GeomShapePtr aGeomShape = theResult->shape();
627     if (aGeomShape.get()) {
628       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
629       // correction of deviation for them should not influence to the application performance
630       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
631       isConstruction = !anExp.more();
632     }
633   }
634   if (isConstruction)
635     aDefault = Config_PropManager::real("Visualization", "construction_deflection");
636   else
637     aDefault = Config_PropManager::real("Visualization", "body_deflection");
638
639   return fabs(aCurrent - aDefault) < 1.e-12;
640 }
641
642 bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) const
643 {
644   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
645   if(!anAttribute || !anAttribute->isInitialized()) {
646     return true;
647   }
648   return fabs(anAttribute->value()) < 1.e-12;
649 }
650
651 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
652 {
653   myDumpBuffer << theChar;
654   return *this;
655 }
656
657 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
658 {
659   myDumpBuffer << theString;
660   return *this;
661 }
662
663 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
664 {
665   myDumpBuffer << theString;
666   return *this;
667 }
668
669 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
670 {
671   myDumpBuffer << (theValue ? "True" : "False");
672   return *this;
673 }
674
675 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
676 {
677   myDumpBuffer << theValue;
678   return *this;
679 }
680
681 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
682 {
683   myDumpBuffer << theValue;
684   return *this;
685 }
686
687 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
688 {
689   importModule("GeomAPI", "GeomAPI_Pnt");
690   myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
691                << thePoint->y() << ", " << thePoint->z() << ")";
692   return *this;
693 }
694
695 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
696 {
697   importModule("GeomAPI", "GeomAPI_Dir");
698   myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
699                << theDir->y() << ", " << theDir->z() << ")";
700   return *this;
701 }
702
703 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
704     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
705 {
706   myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
707   return *this;
708 }
709
710 static void dumpArray(std::ostringstream& theOutput, int theSize,
711                       double* theValues, std::string* theTexts)
712 {
713   for (int i = 0; i < theSize; ++i) {
714     if (i > 0)
715       theOutput << ", ";
716     if (theTexts[i].empty())
717       theOutput << theValues[i];
718     else
719       theOutput << "\"" << theTexts[i] << "\"";
720   }
721 }
722
723 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
724     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
725 {
726   static const int aSize = 3;
727   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
728   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
729   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
730   return *this;
731 }
732
733 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
734     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
735 {
736   static const int aSize = 2;
737   double aValues[aSize] = {thePoint->x(), thePoint->y()};
738   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
739   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
740   return *this;
741 }
742
743 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
744     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
745 {
746   myDumpBuffer << (theAttrBool->value() ? "True" : "False");
747   return *this;
748 }
749
750 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
751     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
752 {
753   std::string aText = theAttrInt->text();
754   if (aText.empty())
755     myDumpBuffer << theAttrInt->value();
756   else
757     myDumpBuffer << "\"" << aText << "\"";
758   return *this;
759 }
760
761 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
762     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
763 {
764   std::string aText = theAttrReal->text();
765   if (aText.empty())
766     myDumpBuffer << theAttrReal->value();
767   else
768     myDumpBuffer << "\"" << aText << "\"";
769   return *this;
770 }
771
772 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
773     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
774 {
775   myDumpBuffer << "\"" << theAttrStr->value() << "\"";
776   return *this;
777 }
778
779 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
780 {
781   myDumpBuffer << name(theFolder);
782
783   // add dumped folder to a stack
784   if (!myNames[theFolder].myIsDumped &&
785      (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theFolder))
786     myEntitiesStack.push(LastDumpedEntity(theFolder, !myNames[theFolder].myIsDefault));
787
788   return *this;
789 }
790
791 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
792 {
793   myDumpBuffer << name(theEntity);
794
795   if (!myNames[theEntity].myIsDumped) {
796     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
797     // store results if they have user-defined names or colors
798     std::list<ResultPtr> aResultsWithNameOrColor;
799     std::list<ResultPtr> allRes;
800     ModelAPI_Tools::allResults(theEntity, allRes);
801     for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
802       if(!myNames[*aRes].myIsDefault || !isDefaultColor(*aRes) ||
803          !isDefaultDeflection(*aRes) || !isDefaultTransparency(*aRes))
804         aResultsWithNameOrColor.push_back(*aRes);
805     }
806     // store just dumped entity to stack
807     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
808       myEntitiesStack.push(
809           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
810   }
811
812   // remove entity from the list of not dumped items
813   myNotDumpedEntities.erase(theEntity);
814   return *this;
815 }
816
817 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
818 {
819   // iterate in the structure of sub-results to the parent
820   ResultPtr aCurRes = theResult;
821   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
822   std::list<int> anIndices; // indexes of results in the parent result, starting from topmost
823   while(aCurRes.get()) {
824     ResultBodyPtr aParent = ModelAPI_Tools::bodyOwner(aCurRes);
825     if (aParent) {
826       anIndices.push_front(ModelAPI_Tools::bodyIndex(aCurRes));
827     } else { // index of the result in the feature
828       std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
829       for(int anIndex = 0; aRes != aFeature->results().cend(); aRes++, anIndex++) {
830         if (*aRes == aCurRes) {
831           anIndices.push_front(anIndex);
832           break;
833         }
834       }
835     }
836     aCurRes = aParent;
837   }
838
839   myDumpBuffer << name(aFeature);
840   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
841     if (anI == anIndices.begin()) {
842       if(*anI == 0) {
843         myDumpBuffer << ".result()";
844       }
845       else {
846         myDumpBuffer << ".results()[" << *anI << "]";
847       }
848     } else {
849       myDumpBuffer << ".subResult(" << *anI << ")";
850     }
851   }
852
853   return *this;
854 }
855
856 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
857 {
858   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
859   if(aFeature.get()) {
860     myDumpBuffer << name(aFeature);
861     return *this;
862   }
863
864   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
865   if(aResult.get()) {
866     *this << aResult;
867     return *this;
868   }
869
870   return *this;
871 }
872
873 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
874 {
875   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
876
877   std::string aWrapperPrefix, aWrapperSuffix;
878   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
879   // In this case we need to cast explicitly feature to appropriate type.
880   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
881   if (isCopy.get() && isCopy->value()) {
882     aWrapperPrefix = featureWrapper(anOwner) + "(";
883     aWrapperSuffix = ")";
884     importModule("SketchAPI");
885   }
886
887   myDumpBuffer << aWrapperPrefix << name(anOwner) << aWrapperSuffix
888                << "." << attributeGetter(anOwner, theAttr->id()) << "()";
889   return *this;
890 }
891
892 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
893     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
894 {
895   if (theRefAttr->isObject())
896     *this << theRefAttr->object();
897   else
898     *this << theRefAttr->attr();
899   return *this;
900 }
901
902 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
903     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
904 {
905   myDumpBuffer << "[";
906   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
907   bool isAdded = false;
908   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
909   for (; anIt != aList.end(); ++anIt) {
910     if (isAdded)
911       myDumpBuffer << ", ";
912     else
913       isAdded = true;
914     if (anIt->first)
915       *this << anIt->first;
916     else if (anIt->second)
917       * this << anIt->second;
918   }
919   myDumpBuffer << "]";
920   return *this;
921 }
922
923 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
924     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
925 {
926   *this << theReference->value();
927   return *this;
928 }
929
930 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
931     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
932 {
933   static const int aThreshold = 2;
934   // if number of elements in the list if greater than a threshold,
935   // dump it in a separate line with specific name
936   std::string aDumped = myDumpBuffer.str();
937   if (aDumped.empty() || theRefList->size() <= aThreshold) {
938     myDumpBuffer << "[";
939     std::list<ObjectPtr> aList = theRefList->list();
940     bool isAdded = false;
941     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
942     for (; anIt != aList.end(); ++anIt) {
943       if (isAdded)
944         myDumpBuffer << ", ";
945       else
946         isAdded = true;
947
948       *this << *anIt;
949     }
950     myDumpBuffer << "]";
951   } else {
952     // clear buffer and store list "as is"
953     myDumpBuffer.str("");
954     *this << theRefList;
955     // save buffer and clear it again
956     std::string aDumpedList = myDumpBuffer.str();
957     myDumpBuffer.str("");
958     // obtain name of list
959     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
960     std::string aListName = name(anOwner) + "_objects";
961     // store all previous data
962     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
963                  << aDumped << aListName;
964   }
965   return *this;
966 }
967
968 static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
969                                      const ResultPtr& theResult,
970                                      const GeomShapePtr& theShape,
971                                      const FeaturePtr& theStartFeature,
972                                      const FeaturePtr& theEndFeature)
973 {
974   DocumentPtr aDoc1 = theStartFeature->document();
975   DocumentPtr aDoc2 = theEndFeature->document();
976
977   std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
978   if (aDoc1 != aDoc2) {
979     std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
980     aFeatures.insert(aFeatures.end(), anAdditionalFeatures.begin(), anAdditionalFeatures.end());
981   }
982
983   CompositeFeaturePtr aLastCompositeFeature;
984
985   std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
986   while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
987     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
988     if (aCompFeat)
989       aLastCompositeFeature = aCompFeat;
990     ++aFIt;
991   }
992
993   // collect the list of composite features, containing the last feature;
994   // these features should be excluded from searching,
995   // because the feature cannot select sub-shapes from its parent
996   std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
997
998   int aNbPossibleSelections = 0;
999   for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
1000     bool isSkipFeature = false;
1001     if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
1002       isSkipFeature = true;
1003     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
1004     if (aCompFeat) {
1005       ResultPartPtr aPartRes =
1006           std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
1007       if (!aPartRes)
1008         aLastCompositeFeature = aCompFeat;
1009       if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
1010         // do not process the parent for the last feature,
1011         // because it cannot select objects from its parent
1012         isSkipFeature = true;
1013       }
1014     }
1015     if (isSkipFeature)
1016       continue;
1017
1018     std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
1019     if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
1020                                                  anApproproate)) {
1021       std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
1022       for (; anApIt != anApproproate.end(); ++anApIt) {
1023         ++aNbPossibleSelections;
1024
1025         // stop if the target shape and result are found
1026         GeomShapePtr aCurShape = anApIt->mySubshape;
1027         if (!aCurShape)
1028           aCurShape = anApIt->myResult->shape();
1029
1030         if (anApIt->myResult->isSame(theResult) && aCurShape->isSame(theShape))
1031           break;
1032       }
1033     }
1034   }
1035   return aNbPossibleSelections;
1036 }
1037
1038 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1039     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
1040 {
1041   myDumpBuffer << "model.selection(";
1042
1043   if(!theAttrSelect->isInitialized()) {
1044     myDumpBuffer << ")";
1045     return *this;
1046   }
1047
1048   GeomShapePtr aShape = theAttrSelect->value();
1049   if(!aShape.get()) {
1050     aShape = theAttrSelect->context()->shape();
1051   }
1052
1053   if(!aShape.get()) {
1054     myDumpBuffer << ")";
1055     return *this;
1056   }
1057
1058   // how to dump selection: construction features are dumped by name always
1059   bool isDumpByGeom = myGeometricalSelection;
1060   FeaturePtr aSelectedFeature;
1061   if (isDumpByGeom) {
1062     ResultPtr aRes = theAttrSelect->context();
1063     FeaturePtr aFeature = theAttrSelect->contextFeature();
1064     if (aRes && !aFeature)
1065       aSelectedFeature = ModelAPI_Feature::feature(aRes->data()->owner());
1066     isDumpByGeom = aSelectedFeature && aSelectedFeature->isInHistory();
1067   }
1068
1069   myDumpBuffer << "\"" << aShape->shapeTypeStr();
1070   if (isDumpByGeom) {
1071     // check the selected item is a ResultPart;
1072     // in this case it is necessary to get shape with full transformation
1073     // for correct calculation of the middle point
1074     ResultPartPtr aResPart =
1075         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
1076     if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
1077       aShape = aResPart->shape();
1078     GeomPointPtr aMiddlePoint = aShape->middlePoint();
1079     // calculate number of features, which could be selected by the same point
1080     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
1081     int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
1082         theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
1083
1084     // produce the index if the number of applicable features is greater than 1
1085     std::string anIndex;
1086     if (aNbPossibleSelections > 1) {
1087       std::ostringstream anOutput;
1088       anOutput << "_" << aNbPossibleSelections;
1089       anIndex = anOutput.str();
1090     }
1091
1092     myDumpBuffer << anIndex << "\", ";
1093     *this << aMiddlePoint;
1094   }
1095   else
1096     myDumpBuffer << "\", \"" << theAttrSelect->namingName() << "\"";
1097   myDumpBuffer << ")";
1098   return *this;
1099 }
1100
1101 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1102     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
1103 {
1104   static const int aThreshold = 2;
1105   // if number of elements in the list if greater than a threshold,
1106   // dump it in a separate line with specific name
1107   std::string aDumped = myDumpBuffer.str();
1108
1109   if (aDumped.empty() || theAttrSelList->size() <= aThreshold) {
1110     myDumpBuffer << "[";
1111
1112     GeomShapePtr aShape;
1113     std::string aShapeTypeStr;
1114
1115     bool isAdded = false;
1116
1117     for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
1118       AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
1119       aShape = anAttribute->value();
1120       if(!aShape.get()) {
1121         ResultPtr aContext = anAttribute->context();
1122         if (aContext.get())
1123           aShape = aContext->shape();
1124       }
1125
1126       if(!aShape.get()) {
1127         continue;
1128       }
1129
1130       if(isAdded) {
1131         myDumpBuffer << ", ";
1132       } else {
1133         isAdded = true;
1134       }
1135       *this << anAttribute;
1136     }
1137
1138     myDumpBuffer << "]";
1139   } else {
1140     // clear buffer and store list "as is"
1141     myDumpBuffer.str("");
1142     *this << theAttrSelList;
1143     // save buffer and clear it again
1144     std::string aDumpedList = myDumpBuffer.str();
1145     myDumpBuffer.str("");
1146     // obtain name of list (the feature may contain several selection lists)
1147     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
1148     std::string aListName = name(anOwner) + "_objects";
1149     std::list<AttributePtr> aSelLists =
1150         anOwner->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
1151     if (aSelLists.size() > 1) {
1152       int anIndex = 1;
1153       for (std::list<AttributePtr>::iterator aSIt = aSelLists.begin();
1154            aSIt != aSelLists.end(); ++aSIt, ++anIndex)
1155         if ((*aSIt).get() == theAttrSelList.get())
1156           break;
1157       std::ostringstream aSStream;
1158       aSStream << aListName << "_" << anIndex;
1159       aListName = aSStream.str();
1160     }
1161     // store all previous data
1162     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
1163                  << aDumped << aListName;
1164   }
1165   return *this;
1166 }
1167
1168 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1169   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
1170 {
1171   myDumpBuffer<<"[";
1172   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
1173     if (anIndex != 0)
1174       myDumpBuffer<<", ";
1175
1176     myDumpBuffer<<"\""<<theArray->value(anIndex)<<"\"";
1177   }
1178
1179   myDumpBuffer<<"]";
1180   return *this;
1181 }
1182
1183 /// Dump std::endl
1184 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
1185                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
1186 {
1187   theDumper.myDumpBuffer << theEndl;
1188
1189   if (!theDumper.myEntitiesStack.empty()) {
1190     bool isCopy;
1191     // all copies have been stored into stack, pop them all
1192     do {
1193       isCopy = false;
1194       // Name for composite feature is dumped when all sub-entities are dumped
1195       // (see method ModelHighAPI_Dumper::processSubs).
1196       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
1197       CompositeFeaturePtr aComposite =
1198           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
1199       if (!aComposite) {
1200         theDumper.dumpEntitySetName();
1201         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
1202         if (aFeature) {
1203           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1204           isCopy = aCopyAttr.get() && aCopyAttr->value();
1205         }
1206       }
1207     } while (isCopy && !theDumper.myEntitiesStack.empty());
1208   }
1209
1210   // store all not-dumped entities first
1211   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
1212   std::string aBufCopy = theDumper.myDumpBuffer.str();
1213   theDumper.clear(true);
1214   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
1215   for (; anIt != aNotDumped.end(); ++anIt) {
1216     // if the feature is composite, dump it with all subs
1217     CompositeFeaturePtr aCompFeat =
1218         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
1219     if (aCompFeat)
1220       theDumper.process(aCompFeat, true);
1221     else {
1222       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1223       theDumper.dumpFeature(aFeature, true);
1224       // dump the Projection feature which produces this "Copy" entity
1225       AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1226       if (aCopyAttr.get() && aCopyAttr->value())
1227       {
1228         const std::set<AttributePtr>& aRefs = aFeature->data()->refsToMe();
1229         std::set<AttributePtr>::iterator aRefIt = aRefs.begin();
1230         for (; aRefIt != aRefs.end(); ++aRefIt)
1231           if ((*aRefIt)->id() == "ProjectedFeature")
1232           { // process projection only
1233             FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
1234             if (anOwner && !theDumper.isDumped(EntityPtr(anOwner)))
1235               theDumper.dumpFeature(anOwner, true);
1236           }
1237       }
1238     }
1239   }
1240
1241   // avoid multiple empty lines
1242   size_t anInd = std::string::npos;
1243   while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
1244     aBufCopy.erase(anInd, 1);
1245   // then store currently dumped string
1246   theDumper.myFullDump << aBufCopy;
1247
1248   // now, store all postponed features
1249   theDumper.dumpPostponed();
1250
1251   return theDumper;
1252 }