Salome HOME
677e31be7b524cbf64ad988ca713e921cd158d88
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "ModelHighAPI_Dumper.h"
21
22 #include <Config_PropManager.h>
23
24 #include <GeomAPI_Pnt.h>
25 #include <GeomAPI_Pnt2d.h>
26 #include <GeomAPI_Dir.h>
27 #include <GeomAPI_ShapeExplorer.h>
28 #include <GeomAPI_ShapeIterator.h>
29 #include <GeomAlgoAPI_NExplode.h>
30
31 #include <GeomDataAPI_Dir.h>
32 #include <GeomDataAPI_Point.h>
33 #include <GeomDataAPI_Point2D.h>
34 #include <GeomDataAPI_Point2DArray.h>
35
36 #include <ModelAPI_AttributeBoolean.h>
37 #include <ModelAPI_AttributeDouble.h>
38 #include <ModelAPI_AttributeDoubleArray.h>
39 #include <ModelAPI_AttributeIntArray.h>
40 #include <ModelAPI_AttributeInteger.h>
41 #include <ModelAPI_AttributeRefAttr.h>
42 #include <ModelAPI_AttributeRefAttrList.h>
43 #include <ModelAPI_AttributeReference.h>
44 #include <ModelAPI_AttributeRefList.h>
45 #include <ModelAPI_AttributeSelection.h>
46 #include <ModelAPI_AttributeSelectionList.h>
47 #include <ModelAPI_AttributeString.h>
48 #include <ModelAPI_AttributeStringArray.h>
49 #include <ModelAPI_CompositeFeature.h>
50 #include <ModelAPI_Document.h>
51 #include <ModelAPI_Entity.h>
52 #include <ModelAPI_Feature.h>
53 #include <ModelAPI_FiltersFeature.h>
54 #include <ModelAPI_Folder.h>
55 #include <ModelAPI_Result.h>
56 #include <ModelAPI_ResultBody.h>
57 #include <ModelAPI_ResultConstruction.h>
58 #include <ModelAPI_ResultGroup.h>
59 #include <ModelAPI_ResultPart.h>
60 #include <ModelAPI_Session.h>
61 #include <ModelAPI_Tools.h>
62
63 #include <ModelGeomAlgo_Shape.h>
64
65 #include <PartSetPlugin_Part.h>
66
67 #include <OSD_OpenFile.hxx>
68
69 #include <fstream>
70
71 // ===========    Implementation of storage of dumped data    ===========
72 static const int THE_DUMP_PRECISION = 16;
73
74 class ModelHighAPI_Dumper::DumpStorageBuffer : public ModelHighAPI_Dumper::DumpStorage
75 {
76 public:
77   void addStorage(const ModelHighAPI_Dumper::DumpStoragePtr& theStorage)
78   { myStorageArray.push_back(theStorage); }
79
80   void clear() { myStorageArray.clear(); }
81
82   bool isBufferEmpty()
83   {
84     return myStorageArray.empty() || myStorageArray.front()->buffer().str().empty();
85   }
86
87   void mergeBuffer()
88   {
89     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
90     for (; anIt != myStorageArray.end(); ++anIt) {
91       // avoid multiple empty lines
92       std::string aBuf = (*anIt)->buffer().str();
93       size_t anInd = std::string::npos;
94       while ((anInd = aBuf.find("\n\n\n")) != std::string::npos)
95         aBuf.erase(anInd, 1);
96
97       (*anIt)->fullDump() << aBuf;
98       (*anIt)->buffer().str("");
99     }
100   }
101
102   void write(const std::string& theValue)
103   {
104     if (myStorageArray.empty())
105       addStorage(DumpStoragePtr(new DumpStorage));
106
107     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
108     for (; anIt != myStorageArray.end(); ++anIt)
109       (*anIt)->buffer() << theValue;
110   }
111
112   DumpStorageBuffer& operator<<(const char theChar)
113   {
114     std::ostringstream out;
115     out << theChar;
116     write(out.str());
117     return *this;
118   }
119
120   DumpStorageBuffer& operator<<(const char* theString)
121   {
122     write(theString);
123     return *this;
124   }
125
126   DumpStorageBuffer& operator<<(const std::string& theString)
127   {
128     write(theString);
129     return *this;
130   }
131
132   DumpStorageBuffer& operator<<(const bool theValue)
133   {
134     std::ostringstream out;
135     out << theValue;
136     write(out.str());
137     return *this;
138   }
139
140   DumpStorageBuffer& operator<<(const int theValue)
141   {
142     std::ostringstream out;
143     out << theValue;
144     write(out.str());
145     return *this;
146   }
147
148   DumpStorageBuffer& operator<<(const double theValue)
149   {
150     std::ostringstream out;
151     out << std::setprecision(THE_DUMP_PRECISION) << theValue;
152     write(out.str());
153     return *this;
154   }
155   /// Dump std::endl
156   friend
157   DumpStorageBuffer& operator<<(DumpStorageBuffer& theBuffer,
158                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
159   {
160     theBuffer.write("\n");
161     return theBuffer;
162   }
163
164   void dumpArray(int theSize, double* theValues, std::string* theTexts)
165   {
166     std::ostringstream anOutput;
167     anOutput << std::setprecision(THE_DUMP_PRECISION);
168     for (int i = 0; i < theSize; ++i) {
169       if (i > 0)
170         anOutput << ", ";
171       if (theTexts[i].empty())
172         anOutput << theValues[i];
173       else
174         anOutput << "\"" << theTexts[i] << "\"";
175     }
176     write(anOutput.str());
177   }
178
179   virtual void write(const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
180   {
181     if (myStorageArray.empty())
182       addStorage(DumpStoragePtr(new DumpStorage));
183
184     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
185     for (; anIt != myStorageArray.end(); ++anIt)
186       (*anIt)->write(theAttrSelect);
187   }
188
189   virtual void reserveBuffer()
190   {
191     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
192     for (; anIt != myStorageArray.end(); ++anIt)
193       (*anIt)->reserveBuffer();
194   }
195
196   virtual void restoreReservedBuffer()
197   {
198     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
199     for (; anIt != myStorageArray.end(); ++anIt)
200       (*anIt)->restoreReservedBuffer();
201   }
202
203   virtual bool exportTo(const std::string& theFilename, const ModulesSet& theUsedModules)
204   {
205     static const std::string THE_EXT = ".py";
206     std::string aFilenameBase = theFilename;
207     if (aFilenameBase.rfind(THE_EXT) == aFilenameBase.size() - THE_EXT.size())
208       aFilenameBase = aFilenameBase.substr(0, aFilenameBase.size() - THE_EXT.size());
209
210     bool isOk = true;
211     std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
212     for (; anIt != myStorageArray.end(); ++anIt) {
213       std::string aFilename = aFilenameBase + (*anIt)->myFilenameSuffix + THE_EXT;
214       isOk = (*anIt)->exportTo(aFilename, theUsedModules) && isOk;
215     }
216     return isOk;
217   }
218
219 private:
220   std::list<ModelHighAPI_Dumper::DumpStoragePtr> myStorageArray;
221 };
222
223
224 ModelHighAPI_Dumper::DumpStorage::DumpStorage(const DumpStorage& theOther)
225   : myFilenameSuffix(theOther.myFilenameSuffix),
226     myDumpBufferHideout(theOther.myDumpBufferHideout)
227 {
228   myFullDump.str(theOther.myFullDump.str());
229   myDumpBuffer.str(theOther.myDumpBuffer.str());
230 }
231
232 const ModelHighAPI_Dumper::DumpStorage&
233 ModelHighAPI_Dumper::DumpStorage::operator=(const ModelHighAPI_Dumper::DumpStorage& theOther)
234 {
235   myFilenameSuffix = theOther.myFilenameSuffix;
236   myFullDump.str(theOther.myFullDump.str());
237   myDumpBuffer.str(theOther.myDumpBuffer.str());
238   myDumpBufferHideout = theOther.myDumpBufferHideout;
239   return *this;
240 }
241
242 void ModelHighAPI_Dumper::DumpStorage::reserveBuffer()
243 {
244   myDumpBufferHideout.push(myDumpBuffer.str());
245   myDumpBuffer.str("");
246 }
247
248 void ModelHighAPI_Dumper::DumpStorage::restoreReservedBuffer()
249 {
250   myDumpBuffer << myDumpBufferHideout.top();
251   myDumpBufferHideout.pop();
252 }
253
254 bool ModelHighAPI_Dumper::DumpStorage::exportTo(const std::string& theFilename,
255                                                 const ModulesSet& theUsedModules)
256 {
257   std::ofstream aFile;
258   OSD_OpenStream(aFile, theFilename.c_str(), std::ofstream::out);
259   if (!aFile.is_open())
260     return false;
261
262   // standard header imported modules
263   for (ModulesSet::const_iterator aModIt = theUsedModules.begin();
264     aModIt != theUsedModules.end(); ++aModIt) {
265     aFile << "from " << *aModIt << " import *" << std::endl;
266   }
267   if (!theUsedModules.empty())
268     aFile << std::endl;
269
270   aFile << "from salome.shaper import model" << std::endl << std::endl;
271   aFile << "model.begin()" << std::endl;
272
273   // dump collected data
274   aFile << myFullDump.str();
275   aFile << myDumpBuffer.str();
276
277   // standard footer
278   aFile << "model.end()" << std::endl;
279   aFile.close();
280
281   return true;
282 }
283
284 static void getShapeAndContext(const AttributeSelectionPtr& theAttrSelect,
285                                GeomShapePtr& theShape, ResultPtr& theContext)
286 {
287   if (theAttrSelect->isInitialized()) {
288     theShape = theAttrSelect->value();
289     theContext = theAttrSelect->context();
290     if (!theShape.get())
291       theShape = theContext->shape();
292
293     if (theAttrSelect->isGeometricalSelection() &&
294         theShape.get() && theShape->shapeType() == GeomAPI_Shape::COMPOUND &&
295         theContext.get() && !theShape->isEqual(theContext->shape()) &&
296         theContext->groupName() != ModelAPI_ResultPart::group() &&
297         theContext->groupName() != ModelAPI_ResultGroup::group()) {
298       GeomAPI_ShapeIterator anIt(theShape);
299       theShape = anIt.current();
300     }
301   }
302 }
303
304 void ModelHighAPI_Dumper::DumpStorage::write(const AttributeSelectionPtr& theAttrSelect)
305 {
306   myDumpBuffer << "model.selection(";
307
308   GeomShapePtr aShape;
309   ResultPtr aContext;
310   getShapeAndContext(theAttrSelect, aShape, aContext);
311
312   if (aShape.get()) {
313     myDumpBuffer << "\"" << aShape->shapeTypeStr() << "\", \""
314                  << theAttrSelect->namingName() << "\"";
315   }
316
317   myDumpBuffer << ")";
318 }
319
320 static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
321                                      const ResultPtr& theResult,
322                                      const GeomShapePtr& theShape,
323                                      const FeaturePtr& theStartFeature,
324                                      const FeaturePtr& theEndFeature)
325 {
326   DocumentPtr aDoc1 = theStartFeature->document();
327   DocumentPtr aDoc2 = theEndFeature->document();
328
329   std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
330   if (aDoc1 != aDoc2) {
331     std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
332     aFeatures.insert(aFeatures.end(), anAdditionalFeatures.begin(), anAdditionalFeatures.end());
333   }
334
335   CompositeFeaturePtr aLastCompositeFeature;
336
337   std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
338   while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
339     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
340     if (aCompFeat)
341       aLastCompositeFeature = aCompFeat;
342     ++aFIt;
343   }
344
345   // collect the list of composite features, containing the last feature;
346   // these features should be excluded from searching,
347   // because the feature cannot select sub-shapes from its parent
348   std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
349
350   int aNbPossibleSelections = 0;
351   for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
352     bool isSkipFeature = false;
353     if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
354       isSkipFeature = true;
355     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
356     if (aCompFeat) {
357       ResultPartPtr aPartRes =
358           std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
359       if (!aPartRes)
360         aLastCompositeFeature = aCompFeat;
361       if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
362         // do not process the parent for the last feature,
363         // because it cannot select objects from its parent
364         isSkipFeature = true;
365       }
366     }
367     if (isSkipFeature)
368       continue;
369
370     std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
371     if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
372                                                  anApproproate)) {
373       std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
374       for (; anApIt != anApproproate.end(); ++anApIt) {
375         ++aNbPossibleSelections;
376
377         // stop if the target shape and result are found
378         GeomShapePtr aCurShape = anApIt->mySubshape;
379         if (!aCurShape)
380           aCurShape = anApIt->myResult->shape();
381
382         if (anApIt->myResult->isSame(theResult) && aCurShape->isSame(theShape))
383           break;
384       }
385     }
386   }
387   return aNbPossibleSelections;
388 }
389
390 void ModelHighAPI_Dumper::DumpStorageGeom::write(const AttributeSelectionPtr& theAttrSelect)
391 {
392   GeomShapePtr aShape;
393   ResultPtr aContext;
394   getShapeAndContext(theAttrSelect, aShape, aContext);
395
396   // how to dump selection: construction features are dumped by name always
397   FeaturePtr aSelectedFeature;
398   FeaturePtr aFeature = theAttrSelect->contextFeature();
399   if (aShape && aContext && !aFeature)
400     aSelectedFeature = ModelAPI_Feature::feature(aContext->data()->owner());
401   bool isDumpByGeom = aSelectedFeature && aSelectedFeature->isInHistory();
402
403   if (isDumpByGeom) {
404     myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr();
405     // check the selected item is a ResultPart;
406     // in this case it is necessary to get shape with full transformation
407     // for correct calculation of the middle point
408     ResultPartPtr aResPart =
409       std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
410     if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
411       aShape = aResPart->shape();
412     GeomPointPtr aMiddlePoint = aShape->middlePoint();
413     // calculate number of features, which could be selected by the same point
414     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
415     int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
416         theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
417
418     // produce the index if the number of applicable features is greater than 1
419     std::string anIndex;
420     if (aNbPossibleSelections > 1) {
421       std::ostringstream anOutput;
422       anOutput << "_" << aNbPossibleSelections;
423       anIndex = anOutput.str();
424     }
425
426     myDumpBuffer << std::setprecision(THE_DUMP_PRECISION)
427                  << anIndex << "\", ("
428                  << aMiddlePoint->x() << ", "
429                  << aMiddlePoint->y() << ", "
430                  << aMiddlePoint->z() << ")";
431     myDumpBuffer << ")";
432   }
433   else
434     DumpStorage::write(theAttrSelect);
435 }
436
437 void ModelHighAPI_Dumper::DumpStorageWeak::write(const AttributeSelectionPtr& theAttrSelect)
438 {
439   GeomShapePtr aShape;
440   ResultPtr aContext;
441   getShapeAndContext(theAttrSelect, aShape, aContext);
442
443   bool aStandardDump = true;
444   if (aShape.get() && aContext.get() &&
445       aShape != aContext->shape()) { // weak naming for local selection only
446     GeomAlgoAPI_NExplode aNExplode(aContext->shape(), aShape->shapeType());
447     int anIndex = aNExplode.index(aShape);
448     if (anIndex != 0) { // found a week-naming index, so, export it
449       myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr() << "\", \""
450                    << theAttrSelect->contextName(aContext) << "\", " << anIndex << ")";
451       aStandardDump = false;
452     }
453   }
454   if (aStandardDump)
455     DumpStorage::write(theAttrSelect);
456 }
457 // ======================================================================
458
459
460 static int gCompositeStackDepth = 0;
461
462 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
463
464 ModelHighAPI_Dumper::ModelHighAPI_Dumper()
465   : myDumpStorage(new DumpStorageBuffer),
466     myDumpPostponedInProgress(false)
467 {
468 }
469
470 ModelHighAPI_Dumper::~ModelHighAPI_Dumper()
471 {
472   delete myDumpStorage;
473 }
474
475 void ModelHighAPI_Dumper::setInstance(ModelHighAPI_Dumper* theDumper)
476 {
477   if (mySelf == 0)
478     mySelf = theDumper;
479 }
480
481 ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
482 {
483   return mySelf;
484 }
485
486 void ModelHighAPI_Dumper::addCustomStorage(const ModelHighAPI_Dumper::DumpStoragePtr& theStorage)
487 {
488   myDumpStorage->addStorage(theStorage);
489 }
490
491 void ModelHighAPI_Dumper::clearCustomStorage()
492 {
493   myDumpStorage->clear();
494
495   myNames.clear();
496   myModules.clear();
497   myFeatureCount.clear();
498   myPostponed.clear();
499   while (!myEntitiesStack.empty())
500     myEntitiesStack.pop();
501   clearNotDumped();
502 }
503
504 void ModelHighAPI_Dumper::clearNotDumped()
505 {
506   myNotDumpedEntities.clear();
507 }
508
509 // Convert string to integer. If the string is not a number, return -1
510 static int toInt(const std::string& theString)
511 {
512   std::string::const_iterator aChar = theString.begin();
513   for (; aChar != theString.end(); ++aChar)
514     if (!std::isdigit(*aChar))
515       break;
516   if (aChar != theString.end())
517     return -1; // not a number
518   return std::stoi(theString);
519 }
520
521 const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
522                                              bool theSaveNotDumped,
523                                              bool theUseEntityName)
524 {
525   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
526   if (aFound != myNames.end())
527     return aFound->second.myCurrentName;
528
529   // entity is not found, store it
530   std::string aName, aKind;
531   bool isDefaultName = false;
532   bool isSaveNotDumped = theSaveNotDumped;
533   std::ostringstream aDefaultName;
534   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
535   if (aFeature) {
536     aName = aFeature->name();
537     aKind = aFeature->getKind();
538   } else {
539     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(theEntity);
540     if (aFolder) {
541       aName = aFolder->data()->name();
542       aKind = ModelAPI_Folder::ID();
543       isSaveNotDumped = false;
544     }
545   }
546
547   ObjectPtr anObject = std::dynamic_pointer_cast<ModelAPI_Object>(theEntity);
548   if (anObject) {
549     DocumentPtr aDoc = anObject->document();
550     std::pair<int, int>& aNbFeatures = myFeatureCount[aDoc][aKind];
551     aNbFeatures.first += 1;
552
553     size_t anIndex = aName.find(aKind);
554     if (anIndex == 0 && aName[aKind.length()] == '_') { // name starts with "FeatureKind_"
555       std::string anIdStr = aName.substr(aKind.length() + 1);
556       int anId = toInt(anIdStr);
557
558       // Check number of already registered objects of such kind. Index of current object
559       // should be the same to identify feature's name as automatically generated.
560       if (aNbFeatures.first == anId && aNbFeatures.second < anId) {
561         // name is not user-defined
562         isDefaultName = true;
563
564         // check there are postponed features of this kind,
565         // dump their names, because the sequence of features may be changed
566         for (std::list<EntityPtr>::const_iterator aPpIt = myPostponed.begin();
567             aPpIt != myPostponed.end(); ++aPpIt) {
568           FeaturePtr aCurFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*aPpIt);
569           if (aCurFeature && aCurFeature->getKind() == aKind) {
570             myNames[*aPpIt].myIsDefault = false;
571             isDefaultName = false;
572           }
573         }
574       }
575
576       if (anId > aNbFeatures.second)
577         aNbFeatures.second = anId;
578     }
579
580     // obtain default name for the feature
581     if (theUseEntityName)
582       aDefaultName << aName;
583     else {
584       int aFullIndex = 0;
585       NbFeaturesMap::const_iterator aFIt = myFeatureCount.begin();
586       for (; aFIt != myFeatureCount.end(); ++aFIt) {
587         std::map<std::string, std::pair<int, int> >::const_iterator aFound =
588           aFIt->second.find(aKind);
589         if (aFound != aFIt->second.end())
590           aFullIndex += aFound->second.first;
591       }
592       aDefaultName << aKind << "_" << aFullIndex;
593     }
594   }
595
596   myNames[theEntity] = EntityName(aDefaultName.str(), aName, isDefaultName);
597   if (isSaveNotDumped)
598     myNotDumpedEntities.insert(theEntity);
599
600   // store names of results
601   if (aFeature)
602     saveResultNames(aFeature);
603
604   return myNames[theEntity].myCurrentName;
605 }
606
607 const std::string& ModelHighAPI_Dumper::parentName(const FeaturePtr& theEntity)
608 {
609   const std::set<AttributePtr>& aRefs = theEntity->data()->refsToMe();
610   std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
611   for (; aRefIt != aRefs.end(); ++aRefIt) {
612     CompositeFeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(
613         ModelAPI_Feature::feature((*aRefIt)->owner()));
614     if (anOwner)
615       return name(anOwner);
616   }
617
618   static const std::string DUMMY;
619   return DUMMY;
620 }
621
622 void ModelHighAPI_Dumper::saveResultNames(const FeaturePtr& theFeature)
623 {
624   // Default name of the feature
625   bool isFeatureDefaultName = myNames[theFeature].myIsDefault;
626
627   // Save only names of results which is not correspond to default feature name
628   const std::list<ResultPtr>& aResults = theFeature->results();
629   std::list<ResultPtr> allRes;
630   ModelAPI_Tools::allResults(theFeature, allRes);
631   for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
632     std::pair<std::string, bool> aName = ModelAPI_Tools::getDefaultName(*aRes);
633     std::string aDefaultName = aName.first;
634     std::string aResName = (*aRes)->data()->name();
635     bool isUserDefined = !(isFeatureDefaultName && aDefaultName == aResName);
636     myNames[*aRes] =
637       EntityName(aResName, (isUserDefined ? aResName : std::string()), !isUserDefined);
638   }
639 }
640
641 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc,
642                                   const std::string& theFileName)
643 {
644   // dump top level document feature
645   static const std::string aDocName("partSet");
646   myNames[theDoc] = EntityName(aDocName, std::string(), true);
647   *this << aDocName << " = model.moduleDocument()" << std::endl;
648
649   // dump subfeatures and store result to file
650   bool isOk = process(theDoc) && myDumpStorage->exportTo(theFileName, myModules);
651   clearCustomStorage();
652   return isOk;
653 }
654
655 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
656 {
657   bool isOk = true;
658   std::list<ObjectPtr> anObjects = theDoc->allObjects();
659   std::list<ObjectPtr>::const_iterator anObjIt = anObjects.begin();
660   // firstly, dump all parameters
661   for (; anObjIt != anObjects.end(); ++ anObjIt) {
662     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
663     if (aFeature)
664       dumpParameter(aFeature);
665   }
666   // dump all other features
667   for (anObjIt = anObjects.begin(); anObjIt != anObjects.end(); ++anObjIt) {
668     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anObjIt);
669     if (aCompFeat) {
670       // iteratively process composite features,
671       // if the composite feature is the last in the document, no need to dump "model.do()" action
672       std::list<ObjectPtr>::const_iterator aNext = anObjIt;
673       isOk = process(aCompFeat, false, ++aNext != anObjects.end()) && isOk;
674     }
675     else if (!isDumped(EntityPtr(*anObjIt))) {
676       // dump folder
677       FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anObjIt);
678       if (aFolder)
679         dumpFolder(aFolder);
680       else {
681         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
682         if (aFeature) // dump common feature
683           dumpFeature(aFeature);
684       }
685     }
686   }
687   // dump folders if any
688   dumpPostponed(true);
689   return isOk;
690 }
691
692 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
693                                   bool isForce, bool isDumpModelDo)
694 {
695   // increase composite features stack
696   ++gCompositeStackDepth;
697   // dump composite itself
698   if (!isDumped(EntityPtr(theComposite)) || isForce)
699     dumpFeature(FeaturePtr(theComposite), isForce);
700
701   // sub-part is processed independently, because it provides separate document
702   if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
703     // dump name of the part if it is different from default
704     if (!myEntitiesStack.empty())
705       dumpEntitySetName();
706
707     // decrease composite features stack because we run into separate document
708     --gCompositeStackDepth;
709
710     ResultPartPtr aPartResult =
711         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
712     if (!aPartResult)
713       return false;
714     DocumentPtr aSubDoc = aPartResult->partDoc();
715     if (!aSubDoc)
716       return false;
717     // set name of document
718     const std::string& aPartName = myNames[theComposite].myCurrentName;
719     std::string aDocName = aPartName + "_doc";
720     myNames[aSubDoc] = EntityName(aDocName, std::string(), true);
721
722     // dump document in a separate line
723     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
724     // dump features in the document
725     bool aRes = process(aSubDoc);
726     if (isDumpModelDo)
727       *this << "model.do()\n";
728     *this << std::endl;
729     return aRes;
730   }
731
732   // dump sub-features
733   bool isOk = processSubs(theComposite);
734   // decrease composite features stack
735   --gCompositeStackDepth;
736
737   return isOk;
738 }
739
740 bool ModelHighAPI_Dumper::processSubs(
741   const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
742   bool theDumpModelDo)
743 {
744   bool isOk = true;
745   // dump all sub-features;
746   bool isSubDumped = false;
747   int aNbSubs = theComposite->numberOfSubs();
748   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
749     FeaturePtr aFeature = theComposite->subFeature(anIndex);
750     if (isDumped(EntityPtr(aFeature)))
751       continue;
752
753     isSubDumped = true;
754     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
755     if (aCompFeat) // iteratively process composite features
756       isOk = process(aCompFeat) && isOk;
757     else
758       dumpFeature(aFeature, true);
759   }
760
761   bool isDumpSetName = !myEntitiesStack.empty() &&
762       myEntitiesStack.top().myEntity == EntityPtr(theComposite);
763   bool isForceModelDo = isSubDumped && isDumpSetName &&
764       (myEntitiesStack.top().myUserName || !myEntitiesStack.top().myResults.empty());
765   // It is necessary for the sketch to create its result when complete (command "model.do()").
766   // This option is set by flat theDumpModelDo.
767   // However, nested sketches are rebuilt by parent feature, so, they do not need
768   // explicit call of "model.do()". This will be controlled by the depth of the stack.
769   if (isForceModelDo || (theDumpModelDo && gCompositeStackDepth <= 1))
770     *this << "model.do()" << std::endl;
771
772   // dump "setName" for composite feature
773   if (isDumpSetName)
774     dumpEntitySetName();
775   return isOk;
776 }
777
778 void ModelHighAPI_Dumper::postpone(const EntityPtr& theEntity)
779 {
780   // keep the name
781   name(theEntity, false);
782   myPostponed.push_back(theEntity);
783 }
784
785 void ModelHighAPI_Dumper::dumpPostponed(bool theDumpFolders)
786 {
787   if (myDumpPostponedInProgress)
788     return;
789
790   myDumpPostponedInProgress = true;
791   // make a copy of postponed entities, because the list will be updated
792   // if some features are not able to be dumped
793   std::list<EntityPtr> aPostponedCopy = myPostponed;
794   myPostponed.clear();
795
796   // iterate over postponed entities and try to dump them
797   std::list<EntityPtr>::const_iterator anIt = aPostponedCopy.begin();
798   for (; anIt != aPostponedCopy.end(); ++anIt) {
799     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anIt);
800     if (aFolder) {
801       if (theDumpFolders)
802         dumpFolder(aFolder);
803       else
804         myPostponed.push_back(*anIt);
805     }
806     else {
807       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
808       if (aFeature)
809         dumpFeature(aFeature, true);
810     }
811   }
812   myDumpPostponedInProgress = false;
813 }
814
815 void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFeatureGet,
816                                                      const FeaturePtr& theSubFeature)
817 {
818   name(theSubFeature, false);
819   myNames[theSubFeature] = EntityName(theSubFeatureGet, theSubFeature->name(), false);
820
821   // store results if they have user-defined names or colors
822   std::list<ResultPtr> aResultsWithNameOrColor;
823   const std::list<ResultPtr>& aResults = theSubFeature->results();
824   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
825   for (; aResIt != aResults.end(); ++aResIt) {
826     std::string aResName = (*aResIt)->data()->name();
827     myNames[*aResIt] = EntityName(aResName, aResName, false);
828     aResultsWithNameOrColor.push_back(*aResIt);
829   }
830
831   // store just dumped entity to stack
832   myEntitiesStack.push(LastDumpedEntity(theSubFeature, true, aResultsWithNameOrColor));
833
834   dumpEntitySetName();
835 }
836
837 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName)
838 {
839   myModules.insert(theModuleName);
840 }
841
842 void ModelHighAPI_Dumper::dumpEntitySetName()
843 {
844   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
845   bool isBufferEmpty = myDumpStorage->isBufferEmpty();
846
847   // dump "setName" for the entity
848   if (aLastDumped.myUserName) {
849     EntityName& anEntityNames = myNames[aLastDumped.myEntity];
850     if (!anEntityNames.myIsDefault)
851       *myDumpStorage << anEntityNames.myCurrentName << ".setName(\""
852                      << anEntityNames.myUserName << "\")\n";
853     // don't dump "setName" for the entity twice
854     anEntityNames.myUserName.clear();
855     anEntityNames.myIsDefault = true;
856   }
857   // dump "setName" for results
858   std::list<ResultPtr>::const_iterator aResIt = aLastDumped.myResults.begin();
859   std::list<ResultPtr>::const_iterator aResEnd = aLastDumped.myResults.end();
860   for (; aResIt != aResEnd; ++aResIt) {
861     // set result name
862     EntityName& anEntityNames = myNames[*aResIt];
863     if (!anEntityNames.myIsDefault) {
864       *this << *aResIt;
865       *myDumpStorage << ".setName(\"" << anEntityNames.myUserName << "\")\n";
866       // don't dump "setName" for the entity twice
867       anEntityNames.myUserName.clear();
868       anEntityNames.myIsDefault = true;
869     }
870     // set result color
871     if (!isDefaultColor(*aResIt)) {
872       AttributeIntArrayPtr aColor = (*aResIt)->data()->intArray(ModelAPI_Result::COLOR_ID());
873       if (aColor && aColor->isInitialized()) {
874         *this << *aResIt;
875         *myDumpStorage << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
876                        << ", " << aColor->value(2) << ")\n";
877       }
878     }
879     // set result deflection
880     if (!isDefaultDeflection(*aResIt)) {
881       AttributeDoublePtr aDeflectionAttr =
882         (*aResIt)->data()->real(ModelAPI_Result::DEFLECTION_ID());
883       if(aDeflectionAttr.get() && aDeflectionAttr->isInitialized()) {
884         *this << *aResIt;
885         *myDumpStorage << ".setDeflection(" << aDeflectionAttr->value() << ")\n";
886       }
887     }
888     // set result transparency
889     if (!isDefaultTransparency(*aResIt)) {
890       AttributeDoublePtr aTransparencyAttr =
891         (*aResIt)->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
892       if(aTransparencyAttr.get() && aTransparencyAttr->isInitialized()) {
893         *this << *aResIt;
894         *myDumpStorage << ".setTransparency(" << aTransparencyAttr->value() << ")\n";
895       }
896     }
897   }
898
899   myNames[aLastDumped.myEntity].myIsDumped = true;
900   myEntitiesStack.pop();
901
902   // clean buffer if it was clear before
903   if (isBufferEmpty)
904     myDumpStorage->mergeBuffer();
905 }
906
907 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
908 {
909   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
910   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
911   return (aFound != myNames.end() && aFound->second.myIsDumped) ||
912          myFeaturesToSkip.find(aFeature) != myFeaturesToSkip.end();
913 }
914
915 bool ModelHighAPI_Dumper::isDumped(const AttributeRefAttrPtr& theRefAttr) const
916 {
917   FeaturePtr aFeature;
918   if (theRefAttr->isObject())
919     aFeature = ModelAPI_Feature::feature(theRefAttr->object());
920   else
921     aFeature = ModelAPI_Feature::feature(theRefAttr->attr()->owner());
922   return aFeature && isDumped(EntityPtr(aFeature));
923 }
924
925 bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
926 {
927   std::list<ObjectPtr> aRefs = theRefList->list();
928   std::list<ObjectPtr>::iterator anIt = aRefs.begin();
929   for (; anIt != aRefs.end(); ++anIt) {
930     FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
931     if (aFeature && !isDumped(EntityPtr(aFeature)))
932       return false;
933   }
934   return true;
935 }
936
937 static bool isSketchSub(const FeaturePtr& theFeature)
938 {
939   static const std::string SKETCH("Sketch");
940   CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theFeature);
941   return anOwner && anOwner->getKind() == SKETCH;
942 }
943
944 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
945 {
946   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
947   if (!aColor || !aColor->isInitialized())
948     return true;
949
950   // check the result belongs to sketch entity, do not dump color in this way
951   ResultConstructionPtr aResConstr =
952       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theResult);
953   if (aResConstr) {
954     FeaturePtr aFeature = ModelAPI_Feature::feature(theResult->data()->owner());
955     if (isSketchSub(aFeature))
956       return true;
957   }
958
959   std::string aSection, aName, aDefault;
960   theResult->colorConfigInfo(aSection, aName, aDefault);
961
962   // dump current color
963   std::ostringstream aColorInfo;
964   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
965
966   return aDefault == aColorInfo.str();
967 }
968
969 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
970 {
971   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
972   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
973     return true;
974   }
975
976   double aCurrent = aDeflectionAttr->value();
977   double aDefault = -1;
978
979   bool isConstruction = false;
980   std::string aResultGroup = theResult->groupName();
981   if (aResultGroup == ModelAPI_ResultConstruction::group())
982     isConstruction = true;
983   else if (aResultGroup == ModelAPI_ResultBody::group()) {
984     GeomShapePtr aGeomShape = theResult->shape();
985     if (aGeomShape.get()) {
986       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
987       // correction of deviation for them should not influence to the application performance
988       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
989       isConstruction = !anExp.more();
990     }
991   }
992   if (isConstruction)
993     aDefault = Config_PropManager::real("Visualization", "construction_deflection");
994   else
995     aDefault = Config_PropManager::real("Visualization", "body_deflection");
996
997   return fabs(aCurrent - aDefault) < 1.e-12;
998 }
999
1000 bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) const
1001 {
1002   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
1003   if(!anAttribute || !anAttribute->isInitialized()) {
1004     return true;
1005   }
1006   return fabs(anAttribute->value()) < 1.e-12;
1007 }
1008
1009 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
1010 {
1011   *myDumpStorage << theChar;
1012   return *this;
1013 }
1014
1015 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
1016 {
1017   *myDumpStorage << theString;
1018   return *this;
1019 }
1020
1021 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
1022 {
1023   *myDumpStorage << theString;
1024   return *this;
1025 }
1026
1027 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
1028 {
1029   *myDumpStorage << (theValue ? "True" : "False");
1030   return *this;
1031 }
1032
1033 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
1034 {
1035   *myDumpStorage << theValue;
1036   return *this;
1037 }
1038
1039 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
1040 {
1041   *myDumpStorage << theValue;
1042   return *this;
1043 }
1044
1045 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
1046 {
1047   importModule("GeomAPI");
1048   *myDumpStorage << "GeomAPI_Pnt(" << thePoint->x() << ", "
1049                  << thePoint->y() << ", " << thePoint->z() << ")";
1050   return *this;
1051 }
1052
1053 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
1054 {
1055   importModule("GeomAPI");
1056   *myDumpStorage << "GeomAPI_Dir(" << theDir->x() << ", "
1057                  << theDir->y() << ", " << theDir->z() << ")";
1058   return *this;
1059 }
1060
1061 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1062     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
1063 {
1064   *myDumpStorage << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
1065   return *this;
1066 }
1067
1068 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1069     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
1070 {
1071   static const int aSize = 3;
1072   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
1073   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
1074   myDumpStorage->dumpArray(aSize, aValues, aTexts);
1075   return *this;
1076 }
1077
1078 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1079     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
1080 {
1081   static const int aSize = 2;
1082   double aValues[aSize] = {thePoint->x(), thePoint->y()};
1083   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
1084   myDumpStorage->dumpArray(aSize, aValues, aTexts);
1085   return *this;
1086 }
1087
1088 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1089   const std::shared_ptr<GeomDataAPI_Point2DArray>& thePointArray)
1090 {
1091   static const int aThreshold = 4;
1092   static bool aDumpAsIs = false;
1093   static std::string aSeparator = "";
1094   // if number of elements in the list if greater than a threshold,
1095   // dump it in a separate line with specific name
1096   int aSize = thePointArray->size();
1097   if (aDumpAsIs || aSize <= aThreshold) {
1098     *myDumpStorage << "[";
1099     GeomPnt2dPtr aPoint = thePointArray->pnt(0);
1100     *myDumpStorage << "(" << aPoint->x() << ", " << aPoint->y() << ")";
1101     for (int anIndex = 1; anIndex < aSize; ++anIndex) {
1102       aPoint = thePointArray->pnt(anIndex);
1103       *myDumpStorage << "," << aSeparator << " (" << aPoint->x() << ", " << aPoint->y() << ")";
1104     }
1105     *myDumpStorage << aSeparator << "]";
1106   }
1107   else {
1108     // name of list
1109     FeaturePtr anOwner = ModelAPI_Feature::feature(thePointArray->owner());
1110     std::string aListName = name(anOwner) + "_" + thePointArray->id();
1111     // reserve dumped buffer and store list "as is"
1112     myDumpStorage->reserveBuffer();
1113     aDumpAsIs = true;
1114     aSeparator = std::string("\n") + std::string(aListName.size() + 3, ' ');
1115     *this << aListName << " = " << thePointArray << "\n";
1116     aDumpAsIs = false;
1117     aSeparator = "";
1118     // append reserved data to the end of the current buffer
1119     myDumpStorage->restoreReservedBuffer();
1120     *myDumpStorage << aListName;
1121   }
1122   return *this;
1123 }
1124
1125 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1126     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
1127 {
1128   *myDumpStorage << (theAttrBool->value() ? "True" : "False");
1129   return *this;
1130 }
1131
1132 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1133     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
1134 {
1135   std::string aText = theAttrInt->text();
1136   if (aText.empty())
1137     *myDumpStorage << theAttrInt->value();
1138   else
1139     *myDumpStorage << "\"" << aText << "\"";
1140   return *this;
1141 }
1142
1143 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1144     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
1145 {
1146   std::string aText = theAttrReal->text();
1147   if (aText.empty())
1148     *myDumpStorage << theAttrReal->value();
1149   else
1150     *myDumpStorage << "\"" << aText << "\"";
1151   return *this;
1152 }
1153
1154 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1155   const std::shared_ptr<ModelAPI_AttributeDoubleArray>& theArray)
1156 {
1157   *myDumpStorage << "[";
1158   int aSize = theArray->size();
1159   if (aSize > 0) {
1160     *myDumpStorage << theArray->value(0);
1161     for (int anIndex = 1; anIndex < aSize; ++anIndex)
1162       *myDumpStorage << ", " << theArray->value(anIndex);
1163   }
1164   *myDumpStorage << "]";
1165   return *this;
1166 }
1167
1168 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1169     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
1170 {
1171   *myDumpStorage << "\"" << theAttrStr->value() << "\"";
1172   return *this;
1173 }
1174
1175 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
1176 {
1177   *myDumpStorage << name(theFolder);
1178
1179   // add dumped folder to a stack
1180   if (!myNames[theFolder].myIsDumped &&
1181      (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theFolder))
1182     myEntitiesStack.push(LastDumpedEntity(theFolder, !myNames[theFolder].myIsDefault));
1183
1184   return *this;
1185 }
1186
1187 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
1188 {
1189   *myDumpStorage << name(theEntity);
1190
1191   if (!myNames[theEntity].myIsDumped) {
1192     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
1193     // store results if they have user-defined names or colors
1194     std::list<ResultPtr> aResultsWithNameOrColor;
1195     std::list<ResultPtr> allRes;
1196     ModelAPI_Tools::allResults(theEntity, allRes);
1197     for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
1198       if(!myNames[*aRes].myIsDefault || !isDefaultColor(*aRes) ||
1199          !isDefaultDeflection(*aRes) || !isDefaultTransparency(*aRes))
1200         aResultsWithNameOrColor.push_back(*aRes);
1201     }
1202     // store just dumped entity to stack
1203     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
1204       myEntitiesStack.push(
1205           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
1206   }
1207
1208   // remove entity from the list of not dumped items
1209   myNotDumpedEntities.erase(theEntity);
1210   return *this;
1211 }
1212
1213 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
1214 {
1215   // iterate in the structure of sub-results to the parent
1216   ResultPtr aCurRes = theResult;
1217   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
1218   std::list<int> anIndices; // indexes of results in the parent result, starting from topmost
1219   while(aCurRes.get()) {
1220     ResultBodyPtr aParent = ModelAPI_Tools::bodyOwner(aCurRes);
1221     if (aParent) {
1222       anIndices.push_front(ModelAPI_Tools::bodyIndex(aCurRes));
1223     } else { // index of the result in the feature
1224       std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
1225       for(int anIndex = 0; aRes != aFeature->results().cend(); aRes++, anIndex++) {
1226         if (*aRes == aCurRes) {
1227           anIndices.push_front(anIndex);
1228           break;
1229         }
1230       }
1231     }
1232     aCurRes = aParent;
1233   }
1234
1235   *myDumpStorage << name(aFeature);
1236   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
1237     if (anI == anIndices.begin()) {
1238       if(*anI == 0) {
1239         *myDumpStorage << ".result()";
1240       }
1241       else {
1242         *myDumpStorage << ".results()[" << *anI << "]";
1243       }
1244     } else {
1245       *myDumpStorage << ".subResult(" << *anI << ")";
1246     }
1247   }
1248
1249   return *this;
1250 }
1251
1252 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::list<ResultPtr>& theResults)
1253 {
1254   *this << "[";
1255   for (std::list<ResultPtr>::const_iterator anIt = theResults.begin();
1256        anIt != theResults.end(); ++anIt) {
1257     if (anIt != theResults.begin())
1258       *this << ", ";
1259     *this << *anIt;
1260   }
1261   *this << "]";
1262   return *this;
1263 }
1264
1265 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
1266 {
1267   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
1268   if(aFeature.get()) {
1269     *myDumpStorage << name(aFeature);
1270     return *this;
1271   }
1272
1273   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
1274   if(aResult.get()) {
1275     *this << aResult;
1276     return *this;
1277   }
1278
1279   return *this;
1280 }
1281
1282 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
1283 {
1284   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
1285
1286   std::string aWrapperPrefix, aWrapperSuffix;
1287   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
1288   // In this case we need to cast explicitly feature to appropriate type.
1289   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
1290   AttributeReferencePtr hasParent = anOwner->reference("ParentFeature");
1291   if ((isCopy.get() && isCopy->value()) || (hasParent && hasParent->value())) {
1292     aWrapperPrefix = featureWrapper(anOwner) + "(";
1293     aWrapperSuffix = ")";
1294     importModule("SketchAPI");
1295   }
1296
1297   *myDumpStorage << aWrapperPrefix << name(anOwner) << aWrapperSuffix
1298                  << "." << attributeGetter(anOwner, theAttr->id()) << "()";
1299   return *this;
1300 }
1301
1302 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1303     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
1304 {
1305   if (theRefAttr->isObject())
1306     *this << theRefAttr->object();
1307   else
1308     *this << theRefAttr->attr();
1309   return *this;
1310 }
1311
1312 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1313     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
1314 {
1315   *myDumpStorage << "[";
1316   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
1317   bool isAdded = false;
1318   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
1319   for (; anIt != aList.end(); ++anIt) {
1320     if (isAdded)
1321       *myDumpStorage << ", ";
1322     else
1323       isAdded = true;
1324     if (anIt->first)
1325       *this << anIt->first;
1326     else if (anIt->second)
1327       * this << anIt->second;
1328   }
1329   *myDumpStorage << "]";
1330   return *this;
1331 }
1332
1333 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1334     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
1335 {
1336   *this << theReference->value();
1337   return *this;
1338 }
1339
1340 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1341     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
1342 {
1343   static const int aThreshold = 2;
1344   static bool aDumpAsIs = false;
1345   // if number of elements in the list if greater than a threshold,
1346   // dump it in a separate line with specific name
1347   if (aDumpAsIs || theRefList->size() <= aThreshold) {
1348     *myDumpStorage << "[";
1349     std::list<ObjectPtr> aList = theRefList->list();
1350     bool isAdded = false;
1351     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
1352     for (; anIt != aList.end(); ++anIt) {
1353       if (isAdded)
1354         *myDumpStorage << ", ";
1355       else
1356         isAdded = true;
1357
1358       *this << *anIt;
1359     }
1360     *myDumpStorage << "]";
1361   } else {
1362     // name of list
1363     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
1364     std::string aListName = name(anOwner) + "_objects";
1365     // reserve dumped buffer and store list "as is"
1366     myDumpStorage->reserveBuffer();
1367     aDumpAsIs = true;
1368     *this << aListName << " = " << theRefList << "\n";
1369     aDumpAsIs = false;
1370     // append reserved data to the end of the current buffer
1371     myDumpStorage->restoreReservedBuffer();
1372     *myDumpStorage << aListName;
1373   }
1374   return *this;
1375 }
1376
1377 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1378     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
1379 {
1380   myDumpStorage->write(theAttrSelect);
1381   return *this;
1382 }
1383
1384 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1385     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
1386 {
1387   static const int aThreshold = 2;
1388   static bool aDumpAsIs = false;
1389   // if number of elements in the list if greater than a threshold,
1390   // dump it in a separate line with specific name
1391   if (aDumpAsIs || theAttrSelList->size() <= aThreshold) {
1392     *myDumpStorage << "[";
1393
1394     GeomShapePtr aShape;
1395     std::string aShapeTypeStr;
1396
1397     bool isAdded = false;
1398
1399     for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
1400       AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
1401       aShape = anAttribute->value();
1402       if(!aShape.get()) {
1403         ResultPtr aContext = anAttribute->context();
1404         if (aContext.get())
1405           aShape = aContext->shape();
1406       }
1407
1408       if(!aShape.get()) {
1409         continue;
1410       }
1411
1412       if(isAdded) {
1413         *myDumpStorage << ", ";
1414       } else {
1415         isAdded = true;
1416       }
1417       *this << anAttribute;
1418     }
1419
1420     // check selection list is obtained by filters
1421     FiltersFeaturePtr aFilters = theAttrSelList->filters();
1422     if (aFilters) {
1423       if (theAttrSelList->size() > 0)
1424         *myDumpStorage << ", ";
1425       dumpFeature(aFilters, true);
1426     }
1427
1428     *myDumpStorage << "]";
1429   } else {
1430     // obtain name of list (the feature may contain several selection lists)
1431     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
1432     std::string aListName = name(anOwner) + "_objects";
1433     std::list<AttributePtr> aSelLists =
1434         anOwner->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
1435     if (aSelLists.size() > 1) {
1436       int anIndex = 1;
1437       for (std::list<AttributePtr>::iterator aSIt = aSelLists.begin();
1438            aSIt != aSelLists.end(); ++aSIt, ++anIndex)
1439         if ((*aSIt).get() == theAttrSelList.get())
1440           break;
1441       std::ostringstream aSStream;
1442       aSStream << aListName << "_" << anIndex;
1443       aListName = aSStream.str();
1444     }
1445     // reserve dumped buffer and store list "as is"
1446     myDumpStorage->reserveBuffer();
1447     aDumpAsIs = true;
1448     *this << aListName << " = " << theAttrSelList << "\n";
1449     aDumpAsIs = false;
1450     // append reserved data to the end of the current buffer
1451     myDumpStorage->restoreReservedBuffer();
1452     *myDumpStorage << aListName;
1453   }
1454   return *this;
1455 }
1456
1457 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1458   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
1459 {
1460   std::ostringstream aBuffer;
1461   aBuffer << "[";
1462   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
1463     if (anIndex != 0)
1464       aBuffer << ", ";
1465
1466     aBuffer << "\"" << theArray->value(anIndex) << "\"";
1467   }
1468   aBuffer << "]";
1469
1470   myDumpStorage->write(aBuffer.str());
1471   return *this;
1472 }
1473
1474 /// Dump std::endl
1475 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
1476                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
1477 {
1478   *theDumper.myDumpStorage << theEndl;
1479
1480   if (!theDumper.myEntitiesStack.empty()) {
1481     bool isCopy;
1482     // all copies have been stored into stack, pop them all
1483     do {
1484       isCopy = false;
1485       // Name for composite feature is dumped when all sub-entities are dumped
1486       // (see method ModelHighAPI_Dumper::processSubs).
1487       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
1488       CompositeFeaturePtr aComposite =
1489           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
1490       if (!aComposite) {
1491         theDumper.dumpEntitySetName();
1492         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
1493         if (aFeature) {
1494           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1495           isCopy = aCopyAttr.get() && aCopyAttr->value();
1496         }
1497       }
1498     } while (isCopy && !theDumper.myEntitiesStack.empty());
1499   }
1500
1501   // store all not-dumped entities first
1502   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
1503   theDumper.myDumpStorage->reserveBuffer();
1504   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
1505   for (; anIt != aNotDumped.end(); ++anIt) {
1506     // if the feature is composite, dump it with all subs
1507     CompositeFeaturePtr aCompFeat =
1508         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
1509     if (aCompFeat)
1510       theDumper.process(aCompFeat, true);
1511     else {
1512       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1513       theDumper.dumpFeature(aFeature, true);
1514       // dump the Projection feature which produces this "Copy" entity
1515       AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1516       if (aCopyAttr.get() && aCopyAttr->value())
1517       {
1518         const std::set<AttributePtr>& aRefs = aFeature->data()->refsToMe();
1519         std::set<AttributePtr>::iterator aRefIt = aRefs.begin();
1520         for (; aRefIt != aRefs.end(); ++aRefIt)
1521           if ((*aRefIt)->id() == "ProjectedFeature")
1522           { // process projection only
1523             FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
1524             if (anOwner && !theDumper.isDumped(EntityPtr(anOwner)))
1525               theDumper.dumpFeature(anOwner, true);
1526           }
1527       }
1528     }
1529   }
1530
1531   // then store the reserved data
1532   theDumper.myDumpStorage->restoreReservedBuffer();
1533   theDumper.myDumpStorage->mergeBuffer();
1534
1535   // now, store all postponed features
1536   theDumper.dumpPostponed();
1537
1538   return theDumper;
1539 }