]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModelHighAPI/ModelHighAPI_Dumper.cpp
Salome HOME
0c28e02ec16fe9fce6f3b8c1a810cb82a978b89d
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.cpp
1 // Copyright (C) 2016-20xx CEA/DEN, EDF R&D -->
2
3 // File:        ModelHighAPI_Dumper.cpp
4 // Created:     1 August 2016
5 // Author:      Artem ZHIDKOV
6
7 //--------------------------------------------------------------------------------------
8 #include "ModelHighAPI_Dumper.h"
9
10 #include <GeomAPI_Pnt.h>
11 #include <GeomAPI_Dir.h>
12
13 #include <GeomDataAPI_Dir.h>
14 #include <GeomDataAPI_Point.h>
15 #include <GeomDataAPI_Point2D.h>
16
17 #include <ModelAPI_AttributeBoolean.h>
18 #include <ModelAPI_AttributeDouble.h>
19 #include <ModelAPI_AttributeInteger.h>
20 #include <ModelAPI_AttributeRefAttr.h>
21 #include <ModelAPI_AttributeRefAttrList.h>
22 #include <ModelAPI_AttributeReference.h>
23 #include <ModelAPI_AttributeRefList.h>
24 #include <ModelAPI_AttributeSelection.h>
25 #include <ModelAPI_AttributeSelectionList.h>
26 #include <ModelAPI_AttributeString.h>
27 #include <ModelAPI_CompositeFeature.h>
28 #include <ModelAPI_Document.h>
29 #include <ModelAPI_Entity.h>
30 #include <ModelAPI_Feature.h>
31 #include <ModelAPI_Result.h>
32 #include <ModelAPI_ResultPart.h>
33
34 #include <PartSetPlugin_Part.h>
35
36 #include <OSD_OpenFile.hxx>
37
38 #include <fstream>
39
40 static int gCompositeStackDepth = 0;
41
42 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
43
44 ModelHighAPI_Dumper::ModelHighAPI_Dumper()
45 {
46   clear();
47 }
48
49 void ModelHighAPI_Dumper::setInstance(ModelHighAPI_Dumper* theDumper)
50 {
51   if (mySelf == 0)
52     mySelf = theDumper;
53 }
54
55 ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
56 {
57   return mySelf;
58 }
59
60 void ModelHighAPI_Dumper::clear(bool bufferOnly)
61 {
62   myDumpBuffer.str("");
63   myDumpBuffer << std::setprecision(16);
64
65   clearNotDumped();
66
67   if (!bufferOnly) {
68     myFullDump.str("");
69     myFullDump << std::setprecision(16);
70
71     myNames.clear();
72     myModules.clear();
73     myFeatureCount.clear();
74     while (!myEntitiesStack.empty())
75       myEntitiesStack.pop();
76   }
77 }
78
79 void ModelHighAPI_Dumper::clearNotDumped()
80 {
81   myNotDumpedEntities.clear();
82 }
83
84 const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
85                                              bool theSaveNotDumped,
86                                              bool theUseEntityName)
87 {
88   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
89   if (aFound != myNames.end())
90     return aFound->second.first;
91
92   // entity is not found, store it
93   std::string aName;
94   std::ostringstream aDefaultName;
95   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
96   if (aFeature) {
97     aName = aFeature->name();
98     const std::string& aKind = aFeature->getKind();
99     DocumentPtr aDoc = aFeature->document();
100     int& aNbFeatures = myFeatureCount[aDoc][aKind];
101     aNbFeatures += 1;
102
103     size_t anIndex = aName.find(aKind);
104     if (anIndex == 0 && aName[aKind.length()] == '_') { // name starts with "FeatureKind_"
105       std::string anIdStr = aName.substr(aKind.length() + 1);
106       int anId = std::stoi(anIdStr);
107
108       // Check number of already registered objects of such kind. Index of current object
109       // should be the same to identify feature's name as automatically generated.
110       if (aNbFeatures == anId) {
111         // name is not user-defined
112         aName.clear();
113       }
114     }
115
116     // obtain default name for the feature
117     if (theUseEntityName)
118       aDefaultName << aName;
119     else {
120       int aFullIndex = 0;
121       NbFeaturesMap::const_iterator aFIt = myFeatureCount.begin();
122       for (; aFIt != myFeatureCount.end(); ++aFIt) {
123         std::map<std::string, int>::const_iterator aFound = aFIt->second.find(aKind);
124         if (aFound != aFIt->second.end())
125           aFullIndex += aFound->second;
126       }
127       aDefaultName << aKind << "_" << aFullIndex;
128     }
129
130     // store names of results
131     saveResultNames(aFeature);
132   }
133
134   myNames[theEntity] = std::pair<std::string, std::string>(aDefaultName.str(), aName);
135   if (theSaveNotDumped)
136     myNotDumpedEntities.insert(theEntity);
137   return myNames[theEntity].first;
138 }
139
140 const std::string& ModelHighAPI_Dumper::parentName(const FeaturePtr& theEntity)
141 {
142   const std::set<AttributePtr>& aRefs = theEntity->data()->refsToMe();
143   std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
144   for (; aRefIt != aRefs.end(); ++aRefIt) {
145     CompositeFeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(
146         ModelAPI_Feature::feature((*aRefIt)->owner()));
147     if (anOwner)
148       return name(anOwner);
149   }
150
151   static const std::string DUMMY;
152   return DUMMY;
153 }
154
155 void ModelHighAPI_Dumper::saveResultNames(const FeaturePtr& theFeature)
156 {
157   std::string aFeatureName = theFeature->name();
158   const std::list<ResultPtr>& aResults = theFeature->results();
159   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
160   for (int i = 1; aResIt != aResults.end(); ++aResIt, ++i) {
161     bool isUserDefined = true;
162     std::string aResName = (*aResIt)->data()->name();
163     size_t anIndex = aResName.find(aFeatureName);
164     if (anIndex == 0) {
165       std::string aSuffix = aResName.substr(aFeatureName.length());
166       if (aSuffix.empty() && i == 1) // first result may not constain index in the name
167         isUserDefined = false;
168       else {
169         if (aSuffix[0] == '_' && std::stoi(aSuffix.substr(1)) == i)
170           isUserDefined = false;
171       }
172     }
173
174     myNames[*aResIt] = std::pair<std::string, std::string>(aResName,
175         isUserDefined ? aResName : std::string());
176   }
177 }
178
179 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc,
180                                   const std::string& theFileName)
181 {
182   // dump top level document feature
183   static const std::string aDocName("partSet");
184   myNames[theDoc] = std::pair<std::string, std::string>(aDocName, std::string());
185   *this << aDocName << " = model.moduleDocument()" << std::endl;
186
187   // dump subfeatures and store result to file
188   return process(theDoc) && exportTo(theFileName);
189 }
190
191 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
192 {
193   bool isOk = true;
194   std::list<FeaturePtr> aFeatures = theDoc->allFeatures();
195   std::list<FeaturePtr>::const_iterator aFeatIt = aFeatures.begin();
196   // firstly, dump all parameters
197   for (; aFeatIt != aFeatures.end(); ++ aFeatIt)
198     dumpParameter(*aFeatIt);
199   // dump all other features
200   for (aFeatIt = aFeatures.begin(); aFeatIt != aFeatures.end(); ++aFeatIt) {
201     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIt);
202     if (aCompFeat) // iteratively process composite features
203       isOk = process(aCompFeat) && isOk;
204     else if (!isDumped(*aFeatIt)) // dump common feature 
205       dumpFeature(*aFeatIt);
206   }
207   return isOk;
208 }
209
210 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite, bool isForce)
211 {
212   // increase composite features stack
213   ++gCompositeStackDepth;
214   // dump composite itself
215   if (!isDumped(theComposite) || isForce)
216     dumpFeature(theComposite, isForce);
217
218   // sub-part is processed independently, because it provides separate document
219   if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
220     // decrease composite features stack because we run into separate document
221     --gCompositeStackDepth;
222
223     ResultPartPtr aPartResult =
224         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
225     if (!aPartResult)
226       return false;
227     DocumentPtr aSubDoc = aPartResult->partDoc();
228     if (!aSubDoc)
229       return false;
230     // set name of document
231     const std::string& aPartName = myNames[theComposite].first;
232     std::string aDocName = aPartName + "_doc";
233     myNames[aSubDoc] = std::pair<std::string, std::string>(aDocName, std::string());
234
235     // dump document in a separate line
236     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
237     // dump features in the document
238     return process(aSubDoc);
239   }
240
241   // dump sub-features
242   bool isOk = processSubs(theComposite);
243   // decrease composite features stack
244   --gCompositeStackDepth;
245
246   return isOk;
247 }
248
249 bool ModelHighAPI_Dumper::processSubs(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
250                                       bool theDumpModelDo)
251 {
252   bool isOk = true;
253   // dump all sub-features;
254   int aNbSubs = theComposite->numberOfSubs();
255   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
256     FeaturePtr aFeature = theComposite->subFeature(anIndex);
257     if (isDumped(aFeature))
258       continue;
259
260     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
261     if (aCompFeat) // iteratively process composite features
262       isOk = process(aCompFeat) && isOk;
263     else
264       dumpFeature(aFeature, true);
265   }
266
267   bool isDumpSetName = !myEntitiesStack.empty() &&
268       myEntitiesStack.top().myEntity == EntityPtr(theComposite);
269   bool isForceModelDo = isDumpSetName &&
270       (myEntitiesStack.top().myUserName || !myEntitiesStack.top().myResultsWithName.empty());
271   // It is necessary for the sketch to create its result when complete (command "model.do()").
272   // This option is set by flat theDumpModelDo.
273   // However, nested sketches are rebuilt by parent feature, so, they do not need
274   // explicit call of "model.do()". This will be controlled by the depth of the stack.
275   if (isForceModelDo || (theDumpModelDo && gCompositeStackDepth <= 1))
276     *this << "model.do()" << std::endl;
277
278   // dump "setName" for composite feature
279   if (isDumpSetName)
280     dumpEntitySetName();
281   return isOk;
282 }
283
284 bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
285 {
286   std::ofstream aFile;
287   OSD_OpenStream(aFile, theFileName.c_str(), std::ofstream::out);
288   if (!aFile.is_open())
289     return false;
290
291   // standard header
292   for (ModulesMap::const_iterator aModIt = myModules.begin();
293        aModIt != myModules.end(); ++aModIt) {
294     aFile << "from " << aModIt->first << " import ";
295     if (aModIt->second.empty() || 
296         aModIt->second.find(std::string()) != aModIt->second.end())
297       aFile << "*"; // import whole module
298     else {
299       // import specific features
300       std::set<std::string>::const_iterator anObjIt = aModIt->second.begin();
301       aFile << *anObjIt;
302       for (++anObjIt; anObjIt != aModIt->second.end(); ++anObjIt)
303         aFile << ", " << *anObjIt;
304     }
305     aFile << std::endl;
306   }
307   if (!myModules.empty())
308     aFile << std::endl;
309
310   aFile << "import model" << std::endl << std::endl;
311   aFile << "model.begin()" << std::endl;
312
313   // dump collected data
314   aFile << myFullDump.str();
315   aFile << myDumpBuffer.str();
316
317   // standard footer
318   aFile << "model.end()" << std::endl;
319
320   aFile.close();
321   clear();
322
323   return true;
324 }
325
326 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName,
327                                        const std::string& theObject)
328 {
329   myModules[theModuleName].insert(theObject);
330 }
331
332 void ModelHighAPI_Dumper::dumpEntitySetName()
333 {
334   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
335
336   // dump "setName" for the entity
337   if (aLastDumped.myUserName) {
338     std::pair<std::string, std::string> anEntityNames = myNames[aLastDumped.myEntity];
339     if (!anEntityNames.second.empty())
340       myDumpBuffer << anEntityNames.first << ".setName(\"" << anEntityNames.second << "\")" << std::endl;
341     anEntityNames.second.clear(); // don't dump "setName" for the entity twice
342   }
343   // dump "setName" for results
344   std::list<ResultPtr>::const_iterator aResIt = aLastDumped.myResultsWithName.begin();
345   std::list<ResultPtr>::const_iterator aResEnd = aLastDumped.myResultsWithName.end();
346   for (; aResIt != aResEnd; ++aResIt) {
347     std::pair<std::string, std::string> anEntityNames = myNames[*aResIt];
348     if (!anEntityNames.second.empty()) {
349       *this << *aResIt;
350       myDumpBuffer << ".result().data().setName(\"" << anEntityNames.second << "\")" << std::endl;
351       anEntityNames.second.clear(); // don't dump "setName" for the entity twice
352     }
353   }
354
355   myEntitiesStack.pop();
356 }
357
358 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
359 {
360   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
361   return aFound != myNames.end();
362 }
363
364 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
365 {
366   myDumpBuffer << theChar;
367   return *this;
368 }
369
370 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
371 {
372   myDumpBuffer << theString;
373   return *this;
374 }
375
376 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
377 {
378   myDumpBuffer << theString;
379   return *this;
380 }
381
382 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
383 {
384   myDumpBuffer << (theValue ? "True" : "False");
385   return *this;
386 }
387
388 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
389 {
390   myDumpBuffer << theValue;
391   return *this;
392 }
393
394 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
395 {
396   myDumpBuffer << theValue;
397   return *this;
398 }
399
400 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
401 {
402   importModule("GeomAPI", "GeomAPI_Pnt");
403   myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
404                << thePoint->y() << ", " << thePoint->z() << ")";
405   return *this;
406 }
407
408 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
409 {
410   importModule("GeomAPI", "GeomAPI_Dir");
411   myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
412                << theDir->y() << ", " << theDir->z() << ")";
413   return *this;
414 }
415
416 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
417     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
418 {
419   myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
420   return *this;
421 }
422
423 static void dumpArray(std::ostringstream& theOutput, int theSize,
424                       double* theValues, std::string* theTexts)
425 {
426   for (int i = 0; i < theSize; ++i) {
427     if (i > 0)
428       theOutput << ", ";
429     if (theTexts[i].empty())
430       theOutput << theValues[i];
431     else
432       theOutput << "\"" << theTexts[i] << "\"";
433   }
434 }
435
436 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
437     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
438 {
439   static const int aSize = 3;
440   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
441   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
442   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
443   return *this;
444 }
445
446 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
447     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
448 {
449   static const int aSize = 2;
450   double aValues[aSize] = {thePoint->x(), thePoint->y()};
451   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
452   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
453   return *this;
454 }
455
456 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
457     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
458 {
459   myDumpBuffer << (theAttrBool->value() ? "True" : "False");
460   return *this;
461 }
462
463 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
464     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
465 {
466   std::string aText = theAttrInt->text();
467   if (aText.empty())
468     myDumpBuffer << theAttrInt->value();
469   else
470     myDumpBuffer << "\"" << aText << "\"";
471   return *this;
472 }
473
474 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
475     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
476 {
477   std::string aText = theAttrReal->text();
478   if (aText.empty())
479     myDumpBuffer << theAttrReal->value();
480   else
481     myDumpBuffer << "\"" << aText << "\"";
482   return *this;
483 }
484
485 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
486     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
487 {
488   myDumpBuffer << "\"" << theAttrStr->value() << "\"";
489   return *this;
490 }
491
492 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
493 {
494   myDumpBuffer << name(theEntity);
495
496   bool isUserDefindName = !myNames[theEntity].second.empty();
497   // store results if they have user-defined names
498   std::list<ResultPtr> aResultsWithUserName;
499   const std::list<ResultPtr>& aResults = theEntity->results();
500   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
501   for (; aResIt != aResults.end(); ++aResIt)
502     if (!myNames[*aResIt].second.empty())
503       aResultsWithUserName.push_back(*aResIt);
504   // store just dumped entity to stack
505   myEntitiesStack.push(LastDumpedEntity(theEntity, isUserDefindName, aResultsWithUserName));
506
507   // remove entity from the list of not dumped items
508   myNotDumpedEntities.erase(theEntity);
509   return *this;
510 }
511
512 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
513 {
514   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
515   int anIndex = 0;
516   std::list<ResultPtr> aResults = aFeature->results();
517   for(std::list<ResultPtr>::const_iterator anIt = aResults.cbegin(); anIt != aResults.cend(); ++anIt, ++anIndex) {
518     if(theResult->isSame(*anIt)) {
519       break;
520     }
521   }
522   myDumpBuffer << name(aFeature) << ".result()[" << anIndex << "]";
523   return *this;
524 }
525
526 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
527 {
528   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
529   if(aFeature.get()) {
530     myDumpBuffer << name(aFeature);
531     return *this;
532   }
533
534   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
535   if(aResult.get()) {
536     *this << aResult;
537     return *this;
538   }
539
540   return *this;
541 }
542
543 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
544 {
545   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
546   myDumpBuffer << name(anOwner) << "." << attributeGetter(anOwner, theAttr->id()) << "()";
547   return *this;
548 }
549
550 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
551     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
552 {
553   if (theRefAttr->isObject())
554     *this << theRefAttr->object();
555   else
556     *this << theRefAttr->attr();
557   return *this;
558 }
559
560 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
561     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
562 {
563   myDumpBuffer << "[";
564   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
565   bool isAdded = false;
566   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
567   for (; anIt != aList.end(); ++anIt) {
568     if (isAdded)
569       myDumpBuffer << ", ";
570     else
571       isAdded = true;
572     if (anIt->first)
573       *this << anIt->first;
574     else if (anIt->second)
575       * this << anIt->second;
576   }
577   myDumpBuffer << "]";
578   return *this;
579 }
580
581 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
582     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
583 {
584   *this << theReference->value();
585   return *this;
586 }
587
588 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
589     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
590 {
591   static const int aThreshold = 2;
592   // if number of elements in the list if greater than a threshold,
593   // dump it in a separate line with specific name
594   std::string aDumped = myDumpBuffer.str();
595   if (aDumped.empty() || theRefList->size() <= aThreshold) {
596     myDumpBuffer << "[";
597     std::list<ObjectPtr> aList = theRefList->list();
598     bool isAdded = false;
599     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
600     for (; anIt != aList.end(); ++anIt) {
601       if (isAdded)
602         myDumpBuffer << ", ";
603       else
604         isAdded = true;
605
606       *this << *anIt;
607     }
608     myDumpBuffer << "]";
609   } else {
610     // clear buffer and store list "as is"
611     myDumpBuffer.str("");
612     *this << theRefList;
613     // save buffer and clear it again
614     std::string aDumpedList = myDumpBuffer.str();
615     myDumpBuffer.str("");
616     // obtain name of list
617     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
618     std::string aListName = name(anOwner) + "_objects";
619     // store all previous data
620     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
621                  << aDumped << aListName;
622   }
623   return *this;
624 }
625
626 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
627     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
628 {
629   myDumpBuffer << "model.selection(";
630
631   if(!theAttrSelect->isInitialized()) {
632     myDumpBuffer << ")";
633     return *this;
634   }
635
636   GeomShapePtr aShape = theAttrSelect->value();
637   if(!aShape.get()) {
638     aShape = theAttrSelect->context()->shape();
639   }
640
641   if(!aShape.get()) {
642     myDumpBuffer << ")";
643     return *this;
644   }
645
646   myDumpBuffer << "\"" << aShape->shapeTypeStr() << "\", \"" << theAttrSelect->namingName() << "\")";
647   return *this;
648 }
649
650 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
651     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
652 {
653   myDumpBuffer << "[";
654
655   GeomShapePtr aShape;
656   std::string aShapeTypeStr;
657
658   bool isAdded = false;
659
660   for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
661     AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
662     aShape = anAttribute->value();
663     if(!aShape.get()) {
664       aShape = anAttribute->context()->shape();
665     }
666
667     if(!aShape.get()) {
668       continue;
669     }
670
671     if(isAdded) {
672       myDumpBuffer << ", ";
673     } else {
674       isAdded = true;
675     }
676     myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr() << "\", \"" << anAttribute->namingName() << "\")";
677   }
678
679   myDumpBuffer << "]";
680   return *this;
681 }
682
683 /// Dump std::endl
684 MODELHIGHAPI_EXPORT
685 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
686                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
687 {
688   theDumper.myDumpBuffer << theEndl;
689
690   if (!theDumper.myEntitiesStack.empty()) {
691     // Name for composite feature is dumped when all sub-entities are dumped
692     // (see method ModelHighAPI_Dumper::processSubs).
693     const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
694     CompositeFeaturePtr aComposite =
695         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
696     if (!aComposite)
697       theDumper.dumpEntitySetName();
698   }
699
700   // store all not-dumped entities first
701   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
702   std::string aBufCopy = theDumper.myDumpBuffer.str();
703   theDumper.clear(true);
704   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
705   for (; anIt != aNotDumped.end(); ++anIt) {
706     // if the feature is composite, dump it with all subs
707     CompositeFeaturePtr aCompFeat =
708         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
709     if (aCompFeat)
710       theDumper.process(aCompFeat, true);
711     else {
712       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
713       theDumper.dumpFeature(aFeature, true);
714     }
715   }
716
717   // avoid multiple empty lines
718   size_t anInd = std::string::npos;
719   while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
720     aBufCopy.erase(anInd, 1);
721   // then store currently dumped string
722   theDumper.myFullDump << aBufCopy;
723
724   return theDumper;
725 }