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