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