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