Salome HOME
6c9fe61b90252255cf3b005b5125f641c1e9234f
[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   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
474   return aFound != myNames.end() || myFeaturesToSkip.find(aFeature) != myFeaturesToSkip.end();
475 }
476
477 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
478 {
479   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
480   if (!aColor || !aColor->isInitialized())
481     return true;
482
483   std::string aSection, aName, aDefault;
484   theResult->colorConfigInfo(aSection, aName, aDefault);
485
486   // dump current color
487   std::ostringstream aColorInfo;
488   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
489
490   return aDefault == aColorInfo.str();
491 }
492
493 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
494 {
495   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
496   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
497     return true;
498   }
499
500   double aCurrent = aDeflectionAttr->value();
501   double aDefault = -1;
502
503   bool isConstruction = false;
504   std::string aResultGroup = theResult->groupName();
505   if (aResultGroup == ModelAPI_ResultConstruction::group())
506     isConstruction = true;
507   else if (aResultGroup == ModelAPI_ResultBody::group()) {
508     GeomShapePtr aGeomShape = theResult->shape();
509     if (aGeomShape.get()) {
510       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
511       // correction of deviation for them should not influence to the application performance
512       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
513       isConstruction = !anExp.more();
514     }
515   }
516   if (isConstruction)
517     aDefault = Config_PropManager::real("Visualization", "construction_deflection",
518                                         ModelAPI_ResultConstruction::DEFAULT_DEFLECTION());
519   else
520     aDefault = Config_PropManager::real("Visualization", "body_deflection",
521                                         ModelAPI_ResultBody::DEFAULT_DEFLECTION());
522
523   return fabs(aCurrent - aDefault) < 1.e-12;
524 }
525
526 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
527 {
528   myDumpBuffer << theChar;
529   return *this;
530 }
531
532 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
533 {
534   myDumpBuffer << theString;
535   return *this;
536 }
537
538 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
539 {
540   myDumpBuffer << theString;
541   return *this;
542 }
543
544 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
545 {
546   myDumpBuffer << (theValue ? "True" : "False");
547   return *this;
548 }
549
550 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
551 {
552   myDumpBuffer << theValue;
553   return *this;
554 }
555
556 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
557 {
558   myDumpBuffer << theValue;
559   return *this;
560 }
561
562 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
563 {
564   importModule("GeomAPI", "GeomAPI_Pnt");
565   myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
566                << thePoint->y() << ", " << thePoint->z() << ")";
567   return *this;
568 }
569
570 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
571 {
572   importModule("GeomAPI", "GeomAPI_Dir");
573   myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
574                << theDir->y() << ", " << theDir->z() << ")";
575   return *this;
576 }
577
578 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
579     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
580 {
581   myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
582   return *this;
583 }
584
585 static void dumpArray(std::ostringstream& theOutput, int theSize,
586                       double* theValues, std::string* theTexts)
587 {
588   for (int i = 0; i < theSize; ++i) {
589     if (i > 0)
590       theOutput << ", ";
591     if (theTexts[i].empty())
592       theOutput << theValues[i];
593     else
594       theOutput << "\"" << theTexts[i] << "\"";
595   }
596 }
597
598 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
599     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
600 {
601   static const int aSize = 3;
602   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
603   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
604   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
605   return *this;
606 }
607
608 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
609     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
610 {
611   static const int aSize = 2;
612   double aValues[aSize] = {thePoint->x(), thePoint->y()};
613   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
614   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
615   return *this;
616 }
617
618 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
619     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
620 {
621   myDumpBuffer << (theAttrBool->value() ? "True" : "False");
622   return *this;
623 }
624
625 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
626     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
627 {
628   std::string aText = theAttrInt->text();
629   if (aText.empty())
630     myDumpBuffer << theAttrInt->value();
631   else
632     myDumpBuffer << "\"" << aText << "\"";
633   return *this;
634 }
635
636 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
637     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
638 {
639   std::string aText = theAttrReal->text();
640   if (aText.empty())
641     myDumpBuffer << theAttrReal->value();
642   else
643     myDumpBuffer << "\"" << aText << "\"";
644   return *this;
645 }
646
647 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
648     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
649 {
650   myDumpBuffer << "\"" << theAttrStr->value() << "\"";
651   return *this;
652 }
653
654 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
655 {
656   myDumpBuffer << name(theEntity);
657
658   if (!myNames[theEntity].myIsDumped) {
659     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
660     // store results if they have user-defined names or colors
661     std::list<ResultPtr> aResultsWithNameOrColor;
662     const std::list<ResultPtr>& aResults = theEntity->results();
663     std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
664     for (; aResIt != aResults.end(); ++aResIt) {
665       if (!myNames[*aResIt].myIsDefault || !isDefaultColor(*aResIt) ||
666           !isDefaultDeflection(*aResIt))
667         aResultsWithNameOrColor.push_back(*aResIt);
668
669       ResultCompSolidPtr aCompSolid =
670           std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*aResIt);
671       if (aCompSolid) {
672         int aNbSubs = aCompSolid->numberOfSubs();
673         for (int i = 0; i < aNbSubs; ++i) {
674           ResultPtr aCurRes = aCompSolid->subResult(i);
675           if (!myNames[aCurRes].myIsDefault || !isDefaultColor(aCurRes) ||
676               !isDefaultDeflection(aCurRes))
677             aResultsWithNameOrColor.push_back(aCurRes);
678         }
679       }
680     }
681     // store just dumped entity to stack
682     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
683       myEntitiesStack.push(
684           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
685   }
686
687   // remove entity from the list of not dumped items
688   myNotDumpedEntities.erase(theEntity);
689   return *this;
690 }
691
692 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
693 {
694   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
695   int anIndex = 0;
696   int aSubIndex = -1;
697   std::list<ResultPtr> aResults = aFeature->results();
698   for(std::list<ResultPtr>::const_iterator
699       anIt = aResults.cbegin(); anIt != aResults.cend(); ++anIt, ++anIndex) {
700     if(theResult->isSame(*anIt)) {
701       break;
702     }
703
704     ResultCompSolidPtr aCompSolid = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(*anIt);
705     if (aCompSolid) {
706       int aNbSubs = aCompSolid->numberOfSubs();
707       for (aSubIndex = 0; aSubIndex < aNbSubs; ++aSubIndex)
708         if (theResult->isSame(aCompSolid->subResult(aSubIndex)))
709           break;
710       if (aSubIndex < aNbSubs)
711         break;
712       aSubIndex = -1;
713     }
714   }
715
716   myDumpBuffer << name(aFeature);
717   if(anIndex == 0) {
718     myDumpBuffer << ".result()";
719   } else {
720     myDumpBuffer << ".results()[" << anIndex << "]";
721   }
722   if (aSubIndex >= 0) {
723     myDumpBuffer << ".subResult(" << aSubIndex << ")";
724   }
725   return *this;
726 }
727
728 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
729 {
730   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
731   if(aFeature.get()) {
732     myDumpBuffer << name(aFeature);
733     return *this;
734   }
735
736   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
737   if(aResult.get()) {
738     *this << aResult;
739     return *this;
740   }
741
742   return *this;
743 }
744
745 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
746 {
747   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
748
749   std::string aWrapperPrefix, aWrapperSuffix;
750   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
751   // In this case we need to cast explicitly feature to appropriate type.
752   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
753   if (isCopy.get() && isCopy->value()) {
754     aWrapperPrefix = featureWrapper(anOwner) + "(";
755     aWrapperSuffix = ")";
756     importModule("SketchAPI");
757   }
758
759   myDumpBuffer << aWrapperPrefix << name(anOwner) << aWrapperSuffix
760                << "." << attributeGetter(anOwner, theAttr->id()) << "()";
761   return *this;
762 }
763
764 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
765     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
766 {
767   if (theRefAttr->isObject())
768     *this << theRefAttr->object();
769   else
770     *this << theRefAttr->attr();
771   return *this;
772 }
773
774 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
775     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
776 {
777   myDumpBuffer << "[";
778   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
779   bool isAdded = false;
780   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
781   for (; anIt != aList.end(); ++anIt) {
782     if (isAdded)
783       myDumpBuffer << ", ";
784     else
785       isAdded = true;
786     if (anIt->first)
787       *this << anIt->first;
788     else if (anIt->second)
789       * this << anIt->second;
790   }
791   myDumpBuffer << "]";
792   return *this;
793 }
794
795 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
796     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
797 {
798   *this << theReference->value();
799   return *this;
800 }
801
802 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
803     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
804 {
805   static const int aThreshold = 2;
806   // if number of elements in the list if greater than a threshold,
807   // dump it in a separate line with specific name
808   std::string aDumped = myDumpBuffer.str();
809   if (aDumped.empty() || theRefList->size() <= aThreshold) {
810     myDumpBuffer << "[";
811     std::list<ObjectPtr> aList = theRefList->list();
812     bool isAdded = false;
813     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
814     for (; anIt != aList.end(); ++anIt) {
815       if (isAdded)
816         myDumpBuffer << ", ";
817       else
818         isAdded = true;
819
820       *this << *anIt;
821     }
822     myDumpBuffer << "]";
823   } else {
824     // clear buffer and store list "as is"
825     myDumpBuffer.str("");
826     *this << theRefList;
827     // save buffer and clear it again
828     std::string aDumpedList = myDumpBuffer.str();
829     myDumpBuffer.str("");
830     // obtain name of list
831     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
832     std::string aListName = name(anOwner) + "_objects";
833     // store all previous data
834     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
835                  << aDumped << aListName;
836   }
837   return *this;
838 }
839
840 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
841     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
842 {
843   myDumpBuffer << "model.selection(";
844
845   if(!theAttrSelect->isInitialized()) {
846     myDumpBuffer << ")";
847     return *this;
848   }
849
850   GeomShapePtr aShape = theAttrSelect->value();
851   if(!aShape.get()) {
852     aShape = theAttrSelect->context()->shape();
853   }
854
855   if(!aShape.get()) {
856     myDumpBuffer << ")";
857     return *this;
858   }
859
860   myDumpBuffer << "\"" << aShape->shapeTypeStr() << "\", \"" <<
861     theAttrSelect->namingName() << "\")";
862   return *this;
863 }
864
865 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
866     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
867 {
868   myDumpBuffer << "[";
869
870   GeomShapePtr aShape;
871   std::string aShapeTypeStr;
872
873   bool isAdded = false;
874
875   for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
876     AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
877     aShape = anAttribute->value();
878     if(!aShape.get()) {
879       aShape = anAttribute->context()->shape();
880     }
881
882     if(!aShape.get()) {
883       continue;
884     }
885
886     if(isAdded) {
887       myDumpBuffer << ", ";
888     } else {
889       isAdded = true;
890     }
891     myDumpBuffer << "model.selection(\"" <<
892       aShape->shapeTypeStr() << "\", \"" << anAttribute->namingName() << "\")";
893   }
894
895   myDumpBuffer << "]";
896   return *this;
897 }
898
899 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
900   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
901 {
902   myDumpBuffer<<"[";
903   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
904     if (anIndex != 0)
905       myDumpBuffer<<", ";
906
907     myDumpBuffer<<"\""<<theArray->value(anIndex)<<"\"";
908   }
909
910   myDumpBuffer<<"]";
911   return *this;
912 }
913
914 /// Dump std::endl
915 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
916                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
917 {
918   theDumper.myDumpBuffer << theEndl;
919
920   if (!theDumper.myEntitiesStack.empty()) {
921     bool isCopy;
922     // all copies have been stored into stack, pop them all
923     do {
924       isCopy = false;
925       // Name for composite feature is dumped when all sub-entities are dumped
926       // (see method ModelHighAPI_Dumper::processSubs).
927       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
928       CompositeFeaturePtr aComposite =
929           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
930       if (!aComposite) {
931         theDumper.dumpEntitySetName();
932         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
933         if (aFeature) {
934           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
935           isCopy = aCopyAttr.get() && aCopyAttr->value();
936         }
937       }
938     } while (isCopy);
939   }
940
941   // store all not-dumped entities first
942   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
943   std::string aBufCopy = theDumper.myDumpBuffer.str();
944   theDumper.clear(true);
945   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
946   for (; anIt != aNotDumped.end(); ++anIt) {
947     // if the feature is composite, dump it with all subs
948     CompositeFeaturePtr aCompFeat =
949         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
950     if (aCompFeat)
951       theDumper.process(aCompFeat, true);
952     else {
953       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
954       theDumper.dumpFeature(aFeature, true);
955     }
956   }
957
958   // avoid multiple empty lines
959   size_t anInd = std::string::npos;
960   while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
961     aBufCopy.erase(anInd, 1);
962   // then store currently dumped string
963   theDumper.myFullDump << aBufCopy;
964
965   return theDumper;
966 }