Salome HOME
Merge remote-tracking branch 'origin/EDF_IMPROVEMENTS_2020'
[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   return isOk;
652 }
653
654 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
655 {
656   bool isOk = true;
657   std::list<ObjectPtr> anObjects = theDoc->allObjects();
658   std::list<ObjectPtr>::const_iterator anObjIt = anObjects.begin();
659   // firstly, dump all parameters
660   for (; anObjIt != anObjects.end(); ++ anObjIt) {
661     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
662     if (aFeature)
663       dumpParameter(aFeature);
664   }
665   // dump all other features
666   for (anObjIt = anObjects.begin(); anObjIt != anObjects.end(); ++anObjIt) {
667     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anObjIt);
668     if (aCompFeat) {
669       // iteratively process composite features,
670       // if the composite feature is the last in the document, no need to dump "model.do()" action
671       std::list<ObjectPtr>::const_iterator aNext = anObjIt;
672       isOk = process(aCompFeat, false, ++aNext != anObjects.end()) && isOk;
673     }
674     else if (!isDumped(EntityPtr(*anObjIt))) {
675       // dump folder
676       FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anObjIt);
677       if (aFolder)
678         dumpFolder(aFolder);
679       else {
680         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
681         if (aFeature) // dump common feature
682           dumpFeature(aFeature);
683       }
684     }
685   }
686   // dump folders if any
687   dumpPostponed(true);
688   return isOk;
689 }
690
691 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
692                                   bool isForce, bool isDumpModelDo)
693 {
694   // increase composite features stack
695   ++gCompositeStackDepth;
696   // dump composite itself
697   if (!isDumped(EntityPtr(theComposite)) || isForce)
698     dumpFeature(FeaturePtr(theComposite), isForce);
699
700   // sub-part is processed independently, because it provides separate document
701   if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
702     // dump name of the part if it is different from default
703     if (!myEntitiesStack.empty())
704       dumpEntitySetName();
705
706     // decrease composite features stack because we run into separate document
707     --gCompositeStackDepth;
708
709     ResultPartPtr aPartResult =
710         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
711     if (!aPartResult)
712       return false;
713     DocumentPtr aSubDoc = aPartResult->partDoc();
714     if (!aSubDoc)
715       return false;
716     // set name of document
717     const std::string& aPartName = myNames[theComposite].myCurrentName;
718     std::string aDocName = aPartName + "_doc";
719     myNames[aSubDoc] = EntityName(aDocName, std::string(), true);
720
721     // dump document in a separate line
722     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
723     // dump features in the document
724     bool aRes = process(aSubDoc);
725     if (isDumpModelDo)
726       *this << "model.do()\n";
727     *this << std::endl;
728     return aRes;
729   }
730
731   // dump sub-features
732   bool isOk = processSubs(theComposite);
733   // decrease composite features stack
734   --gCompositeStackDepth;
735
736   return isOk;
737 }
738
739 bool ModelHighAPI_Dumper::processSubs(
740   const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
741   bool theDumpModelDo)
742 {
743   bool isOk = true;
744   // dump all sub-features;
745   bool isSubDumped = false;
746   int aNbSubs = theComposite->numberOfSubs();
747   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
748     FeaturePtr aFeature = theComposite->subFeature(anIndex);
749     if (isDumped(EntityPtr(aFeature)))
750       continue;
751
752     isSubDumped = true;
753     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
754     if (aCompFeat) // iteratively process composite features
755       isOk = process(aCompFeat) && isOk;
756     else
757       dumpFeature(aFeature, true);
758   }
759
760   bool isDumpSetName = !myEntitiesStack.empty() &&
761       myEntitiesStack.top().myEntity == EntityPtr(theComposite);
762   bool isForceModelDo = isSubDumped && isDumpSetName &&
763       (myEntitiesStack.top().myUserName || !myEntitiesStack.top().myResults.empty());
764   // It is necessary for the sketch to create its result when complete (command "model.do()").
765   // This option is set by flat theDumpModelDo.
766   // However, nested sketches are rebuilt by parent feature, so, they do not need
767   // explicit call of "model.do()". This will be controlled by the depth of the stack.
768   if (isForceModelDo || (theDumpModelDo && gCompositeStackDepth <= 1))
769     *this << "model.do()" << std::endl;
770
771   // dump "setName" for composite feature
772   if (isDumpSetName)
773     dumpEntitySetName();
774   return isOk;
775 }
776
777 void ModelHighAPI_Dumper::postpone(const EntityPtr& theEntity)
778 {
779   // keep the name
780   name(theEntity, false);
781   myPostponed.push_back(theEntity);
782 }
783
784 void ModelHighAPI_Dumper::dumpPostponed(bool theDumpFolders)
785 {
786   if (myDumpPostponedInProgress)
787     return;
788
789   myDumpPostponedInProgress = true;
790   // make a copy of postponed entities, because the list will be updated
791   // if some features are not able to be dumped
792   std::list<EntityPtr> aPostponedCopy = myPostponed;
793   myPostponed.clear();
794
795   // iterate over postponed entities and try to dump them
796   std::list<EntityPtr>::const_iterator anIt = aPostponedCopy.begin();
797   for (; anIt != aPostponedCopy.end(); ++anIt) {
798     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anIt);
799     if (aFolder) {
800       if (theDumpFolders)
801         dumpFolder(aFolder);
802       else
803         myPostponed.push_back(*anIt);
804     }
805     else {
806       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
807       if (aFeature)
808         dumpFeature(aFeature, true);
809     }
810   }
811   myDumpPostponedInProgress = false;
812 }
813
814 void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFeatureGet,
815                                                      const FeaturePtr& theSubFeature)
816 {
817   name(theSubFeature, false);
818   myNames[theSubFeature] = EntityName(theSubFeatureGet, theSubFeature->name(), false);
819
820   // store results if they have user-defined names or colors
821   std::list<ResultPtr> aResultsWithNameOrColor;
822   const std::list<ResultPtr>& aResults = theSubFeature->results();
823   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
824   for (; aResIt != aResults.end(); ++aResIt) {
825     std::string aResName = (*aResIt)->data()->name();
826     myNames[*aResIt] = EntityName(aResName, aResName, false);
827     aResultsWithNameOrColor.push_back(*aResIt);
828   }
829
830   // store just dumped entity to stack
831   myEntitiesStack.push(LastDumpedEntity(theSubFeature, true, aResultsWithNameOrColor));
832
833   dumpEntitySetName();
834 }
835
836 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName)
837 {
838   myModules.insert(theModuleName);
839 }
840
841 void ModelHighAPI_Dumper::dumpEntitySetName()
842 {
843   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
844   bool isBufferEmpty = myDumpStorage->isBufferEmpty();
845
846   // dump "setName" for the entity
847   if (aLastDumped.myUserName) {
848     EntityName& anEntityNames = myNames[aLastDumped.myEntity];
849     if (!anEntityNames.myIsDefault)
850       *myDumpStorage << anEntityNames.myCurrentName << ".setName(\""
851                      << anEntityNames.myUserName << "\")\n";
852     // don't dump "setName" for the entity twice
853     anEntityNames.myUserName.clear();
854     anEntityNames.myIsDefault = true;
855   }
856   // dump "setName" for results
857   std::list<ResultPtr>::const_iterator aResIt = aLastDumped.myResults.begin();
858   std::list<ResultPtr>::const_iterator aResEnd = aLastDumped.myResults.end();
859   for (; aResIt != aResEnd; ++aResIt) {
860     // set result name
861     EntityName& anEntityNames = myNames[*aResIt];
862     if (!anEntityNames.myIsDefault) {
863       *this << *aResIt;
864       *myDumpStorage << ".setName(\"" << anEntityNames.myUserName << "\")\n";
865       // don't dump "setName" for the entity twice
866       anEntityNames.myUserName.clear();
867       anEntityNames.myIsDefault = true;
868     }
869     // set result color
870     if (!isDefaultColor(*aResIt)) {
871       AttributeIntArrayPtr aColor = (*aResIt)->data()->intArray(ModelAPI_Result::COLOR_ID());
872       if (aColor && aColor->isInitialized()) {
873         *this << *aResIt;
874         *myDumpStorage << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
875                        << ", " << aColor->value(2) << ")\n";
876       }
877     }
878     // set result deflection
879     if (!isDefaultDeflection(*aResIt)) {
880       AttributeDoublePtr aDeflectionAttr =
881         (*aResIt)->data()->real(ModelAPI_Result::DEFLECTION_ID());
882       if(aDeflectionAttr.get() && aDeflectionAttr->isInitialized()) {
883         *this << *aResIt;
884         *myDumpStorage << ".setDeflection(" << aDeflectionAttr->value() << ")\n";
885       }
886     }
887     // set result transparency
888     if (!isDefaultTransparency(*aResIt)) {
889       AttributeDoublePtr aTransparencyAttr =
890         (*aResIt)->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
891       if(aTransparencyAttr.get() && aTransparencyAttr->isInitialized()) {
892         *this << *aResIt;
893         *myDumpStorage << ".setTransparency(" << aTransparencyAttr->value() << ")\n";
894       }
895     }
896   }
897
898   myNames[aLastDumped.myEntity].myIsDumped = true;
899   myEntitiesStack.pop();
900
901   // clean buffer if it was clear before
902   if (isBufferEmpty)
903     myDumpStorage->mergeBuffer();
904 }
905
906 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
907 {
908   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
909   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
910   return (aFound != myNames.end() && aFound->second.myIsDumped) ||
911          myFeaturesToSkip.find(aFeature) != myFeaturesToSkip.end();
912 }
913
914 bool ModelHighAPI_Dumper::isDumped(const AttributeRefAttrPtr& theRefAttr) const
915 {
916   FeaturePtr aFeature;
917   if (theRefAttr->isObject())
918     aFeature = ModelAPI_Feature::feature(theRefAttr->object());
919   else
920     aFeature = ModelAPI_Feature::feature(theRefAttr->attr()->owner());
921   return aFeature && isDumped(EntityPtr(aFeature));
922 }
923
924 bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
925 {
926   std::list<ObjectPtr> aRefs = theRefList->list();
927   std::list<ObjectPtr>::iterator anIt = aRefs.begin();
928   for (; anIt != aRefs.end(); ++anIt) {
929     FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
930     if (aFeature && !isDumped(EntityPtr(aFeature)))
931       return false;
932   }
933   return true;
934 }
935
936 static bool isSketchSub(const FeaturePtr& theFeature)
937 {
938   static const std::string SKETCH("Sketch");
939   CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theFeature);
940   return anOwner && anOwner->getKind() == SKETCH;
941 }
942
943 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
944 {
945   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
946   if (!aColor || !aColor->isInitialized())
947     return true;
948
949   // check the result belongs to sketch entity, do not dump color in this way
950   ResultConstructionPtr aResConstr =
951       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theResult);
952   if (aResConstr) {
953     FeaturePtr aFeature = ModelAPI_Feature::feature(theResult->data()->owner());
954     if (isSketchSub(aFeature))
955       return true;
956   }
957
958   std::string aSection, aName, aDefault;
959   theResult->colorConfigInfo(aSection, aName, aDefault);
960
961   // dump current color
962   std::ostringstream aColorInfo;
963   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
964
965   return aDefault == aColorInfo.str();
966 }
967
968 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
969 {
970   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
971   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
972     return true;
973   }
974
975   double aCurrent = aDeflectionAttr->value();
976   double aDefault = -1;
977
978   bool isConstruction = false;
979   std::string aResultGroup = theResult->groupName();
980   if (aResultGroup == ModelAPI_ResultConstruction::group())
981     isConstruction = true;
982   else if (aResultGroup == ModelAPI_ResultBody::group()) {
983     GeomShapePtr aGeomShape = theResult->shape();
984     if (aGeomShape.get()) {
985       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
986       // correction of deviation for them should not influence to the application performance
987       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
988       isConstruction = !anExp.more();
989     }
990   }
991   if (isConstruction)
992     aDefault = Config_PropManager::real("Visualization", "construction_deflection");
993   else
994     aDefault = Config_PropManager::real("Visualization", "body_deflection");
995
996   return fabs(aCurrent - aDefault) < 1.e-12;
997 }
998
999 bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) const
1000 {
1001   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
1002   if(!anAttribute || !anAttribute->isInitialized()) {
1003     return true;
1004   }
1005   return fabs(anAttribute->value()) < 1.e-12;
1006 }
1007
1008 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
1009 {
1010   *myDumpStorage << theChar;
1011   return *this;
1012 }
1013
1014 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
1015 {
1016   *myDumpStorage << theString;
1017   return *this;
1018 }
1019
1020 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
1021 {
1022   *myDumpStorage << theString;
1023   return *this;
1024 }
1025
1026 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
1027 {
1028   *myDumpStorage << (theValue ? "True" : "False");
1029   return *this;
1030 }
1031
1032 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
1033 {
1034   *myDumpStorage << theValue;
1035   return *this;
1036 }
1037
1038 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
1039 {
1040   *myDumpStorage << theValue;
1041   return *this;
1042 }
1043
1044 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
1045 {
1046   importModule("GeomAPI");
1047   *myDumpStorage << "GeomAPI_Pnt(" << thePoint->x() << ", "
1048                  << thePoint->y() << ", " << thePoint->z() << ")";
1049   return *this;
1050 }
1051
1052 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
1053 {
1054   importModule("GeomAPI");
1055   *myDumpStorage << "GeomAPI_Dir(" << theDir->x() << ", "
1056                  << theDir->y() << ", " << theDir->z() << ")";
1057   return *this;
1058 }
1059
1060 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1061     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
1062 {
1063   *myDumpStorage << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
1064   return *this;
1065 }
1066
1067 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1068     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
1069 {
1070   static const int aSize = 3;
1071   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
1072   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
1073   myDumpStorage->dumpArray(aSize, aValues, aTexts);
1074   return *this;
1075 }
1076
1077 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1078     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
1079 {
1080   static const int aSize = 2;
1081   double aValues[aSize] = {thePoint->x(), thePoint->y()};
1082   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
1083   myDumpStorage->dumpArray(aSize, aValues, aTexts);
1084   return *this;
1085 }
1086
1087 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1088   const std::shared_ptr<GeomDataAPI_Point2DArray>& thePointArray)
1089 {
1090   static const int aThreshold = 4;
1091   static bool aDumpAsIs = false;
1092   static std::string aSeparator = "";
1093   // if number of elements in the list if greater than a threshold,
1094   // dump it in a separate line with specific name
1095   int aSize = thePointArray->size();
1096   if (aDumpAsIs || aSize <= aThreshold) {
1097     *myDumpStorage << "[";
1098     GeomPnt2dPtr aPoint = thePointArray->pnt(0);
1099     *myDumpStorage << "(" << aPoint->x() << ", " << aPoint->y() << ")";
1100     for (int anIndex = 1; anIndex < aSize; ++anIndex) {
1101       aPoint = thePointArray->pnt(anIndex);
1102       *myDumpStorage << "," << aSeparator << " (" << aPoint->x() << ", " << aPoint->y() << ")";
1103     }
1104     *myDumpStorage << aSeparator << "]";
1105   }
1106   else {
1107     // name of list
1108     FeaturePtr anOwner = ModelAPI_Feature::feature(thePointArray->owner());
1109     std::string aListName = name(anOwner) + "_" + thePointArray->id();
1110     // reserve dumped buffer and store list "as is"
1111     myDumpStorage->reserveBuffer();
1112     aDumpAsIs = true;
1113     aSeparator = std::string("\n") + std::string(aListName.size() + 3, ' ');
1114     *this << aListName << " = " << thePointArray << "\n";
1115     aDumpAsIs = false;
1116     aSeparator = "";
1117     // append reserved data to the end of the current buffer
1118     myDumpStorage->restoreReservedBuffer();
1119     *myDumpStorage << aListName;
1120   }
1121   return *this;
1122 }
1123
1124 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1125     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
1126 {
1127   *myDumpStorage << (theAttrBool->value() ? "True" : "False");
1128   return *this;
1129 }
1130
1131 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1132     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
1133 {
1134   std::string aText = theAttrInt->text();
1135   if (aText.empty())
1136     *myDumpStorage << theAttrInt->value();
1137   else
1138     *myDumpStorage << "\"" << aText << "\"";
1139   return *this;
1140 }
1141
1142 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1143     const std::shared_ptr<ModelAPI_AttributeIntArray>& theArray)
1144 {
1145   *myDumpStorage << "[";
1146   int aSize = theArray->size();
1147   if (aSize > 0) {
1148     *myDumpStorage << theArray->value(0);
1149     for (int anIndex = 1; anIndex < aSize; ++anIndex)
1150       *myDumpStorage << ", " << theArray->value(anIndex);
1151   }
1152   *myDumpStorage << "]";
1153   return *this;
1154 }
1155
1156 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1157     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
1158 {
1159   std::string aText = theAttrReal->text();
1160   if (aText.empty())
1161     *myDumpStorage << theAttrReal->value();
1162   else
1163     *myDumpStorage << "\"" << aText << "\"";
1164   return *this;
1165 }
1166
1167 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1168   const std::shared_ptr<ModelAPI_AttributeDoubleArray>& theArray)
1169 {
1170   *myDumpStorage << "[";
1171   int aSize = theArray->size();
1172   if (aSize > 0) {
1173     *myDumpStorage << theArray->value(0);
1174     for (int anIndex = 1; anIndex < aSize; ++anIndex)
1175       *myDumpStorage << ", " << theArray->value(anIndex);
1176   }
1177   *myDumpStorage << "]";
1178   return *this;
1179 }
1180
1181 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1182     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
1183 {
1184   *myDumpStorage << "\"" << theAttrStr->value() << "\"";
1185   return *this;
1186 }
1187
1188 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
1189 {
1190   *myDumpStorage << name(theFolder);
1191
1192   // add dumped folder to a stack
1193   if (!myNames[theFolder].myIsDumped &&
1194      (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theFolder))
1195     myEntitiesStack.push(LastDumpedEntity(theFolder, !myNames[theFolder].myIsDefault));
1196
1197   return *this;
1198 }
1199
1200 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
1201 {
1202   *myDumpStorage << name(theEntity);
1203
1204   if (!myNames[theEntity].myIsDumped) {
1205     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
1206     // store results if they have user-defined names or colors
1207     std::list<ResultPtr> aResultsWithNameOrColor;
1208     std::list<ResultPtr> allRes;
1209     ModelAPI_Tools::allResults(theEntity, allRes);
1210     for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
1211       if(!myNames[*aRes].myIsDefault || !isDefaultColor(*aRes) ||
1212          !isDefaultDeflection(*aRes) || !isDefaultTransparency(*aRes))
1213         aResultsWithNameOrColor.push_back(*aRes);
1214     }
1215     // store just dumped entity to stack
1216     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
1217       myEntitiesStack.push(
1218           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
1219   }
1220
1221   // remove entity from the list of not dumped items
1222   myNotDumpedEntities.erase(theEntity);
1223   return *this;
1224 }
1225
1226 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
1227 {
1228   // iterate in the structure of sub-results to the parent
1229   ResultPtr aCurRes = theResult;
1230   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
1231   std::list<int> anIndices; // indexes of results in the parent result, starting from topmost
1232   while(aCurRes.get()) {
1233     ResultBodyPtr aParent = ModelAPI_Tools::bodyOwner(aCurRes);
1234     if (aParent) {
1235       anIndices.push_front(ModelAPI_Tools::bodyIndex(aCurRes));
1236     } else { // index of the result in the feature
1237       std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
1238       for(int anIndex = 0; aRes != aFeature->results().cend(); aRes++, anIndex++) {
1239         if (*aRes == aCurRes) {
1240           anIndices.push_front(anIndex);
1241           break;
1242         }
1243       }
1244     }
1245     aCurRes = aParent;
1246   }
1247
1248   *myDumpStorage << name(aFeature);
1249   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
1250     if (anI == anIndices.begin()) {
1251       if(*anI == 0) {
1252         *myDumpStorage << ".result()";
1253       }
1254       else {
1255         *myDumpStorage << ".results()[" << *anI << "]";
1256       }
1257     } else {
1258       *myDumpStorage << ".subResult(" << *anI << ")";
1259     }
1260   }
1261
1262   return *this;
1263 }
1264
1265 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::list<ResultPtr>& theResults)
1266 {
1267   *this << "[";
1268   for (std::list<ResultPtr>::const_iterator anIt = theResults.begin();
1269        anIt != theResults.end(); ++anIt) {
1270     if (anIt != theResults.begin())
1271       *this << ", ";
1272     *this << *anIt;
1273   }
1274   *this << "]";
1275   return *this;
1276 }
1277
1278 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
1279 {
1280   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
1281   if(aFeature.get()) {
1282     *myDumpStorage << name(aFeature);
1283     return *this;
1284   }
1285
1286   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
1287   if(aResult.get()) {
1288     *this << aResult;
1289     return *this;
1290   }
1291
1292   return *this;
1293 }
1294
1295 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
1296 {
1297   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
1298
1299   std::string aWrapperPrefix, aWrapperSuffix;
1300   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
1301   // In this case we need to cast explicitly feature to appropriate type.
1302   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
1303   AttributeReferencePtr hasParent = anOwner->reference("ParentFeature");
1304   if ((isCopy.get() && isCopy->value()) || (hasParent && hasParent->value())) {
1305     aWrapperPrefix = featureWrapper(anOwner) + "(";
1306     aWrapperSuffix = ")";
1307     importModule("SketchAPI");
1308   }
1309
1310   *myDumpStorage << aWrapperPrefix << name(anOwner) << aWrapperSuffix
1311                  << "." << attributeGetter(anOwner, theAttr->id()) << "()";
1312   return *this;
1313 }
1314
1315 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1316     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
1317 {
1318   if (theRefAttr->isObject())
1319     *this << theRefAttr->object();
1320   else
1321     *this << theRefAttr->attr();
1322   return *this;
1323 }
1324
1325 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1326     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
1327 {
1328   *myDumpStorage << "[";
1329   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
1330   bool isAdded = false;
1331   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
1332   for (; anIt != aList.end(); ++anIt) {
1333     if (isAdded)
1334       *myDumpStorage << ", ";
1335     else
1336       isAdded = true;
1337     if (anIt->first)
1338       *this << anIt->first;
1339     else if (anIt->second)
1340       * this << anIt->second;
1341   }
1342   *myDumpStorage << "]";
1343   return *this;
1344 }
1345
1346 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1347     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
1348 {
1349   *this << theReference->value();
1350   return *this;
1351 }
1352
1353 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1354     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
1355 {
1356   static const int aThreshold = 2;
1357   static bool aDumpAsIs = false;
1358   // if number of elements in the list if greater than a threshold,
1359   // dump it in a separate line with specific name
1360   if (aDumpAsIs || theRefList->size() <= aThreshold) {
1361     *myDumpStorage << "[";
1362     std::list<ObjectPtr> aList = theRefList->list();
1363     bool isAdded = false;
1364     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
1365     for (; anIt != aList.end(); ++anIt) {
1366       if (isAdded)
1367         *myDumpStorage << ", ";
1368       else
1369         isAdded = true;
1370
1371       *this << *anIt;
1372     }
1373     *myDumpStorage << "]";
1374   } else {
1375     // name of list
1376     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
1377     std::string aListName = name(anOwner) + "_objects";
1378     // reserve dumped buffer and store list "as is"
1379     myDumpStorage->reserveBuffer();
1380     aDumpAsIs = true;
1381     *this << aListName << " = " << theRefList << "\n";
1382     aDumpAsIs = false;
1383     // append reserved data to the end of the current buffer
1384     myDumpStorage->restoreReservedBuffer();
1385     *myDumpStorage << aListName;
1386   }
1387   return *this;
1388 }
1389
1390 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1391     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
1392 {
1393   myDumpStorage->write(theAttrSelect);
1394   return *this;
1395 }
1396
1397 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1398     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
1399 {
1400   static const int aThreshold = 2;
1401   static bool aDumpAsIs = false;
1402   // if number of elements in the list if greater than a threshold,
1403   // dump it in a separate line with specific name
1404   if (aDumpAsIs || theAttrSelList->size() <= aThreshold) {
1405     *myDumpStorage << "[";
1406
1407     GeomShapePtr aShape;
1408     std::string aShapeTypeStr;
1409
1410     bool isAdded = false;
1411
1412     for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
1413       AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
1414       aShape = anAttribute->value();
1415       if(!aShape.get()) {
1416         ResultPtr aContext = anAttribute->context();
1417         if (aContext.get())
1418           aShape = aContext->shape();
1419       }
1420
1421       if(!aShape.get()) {
1422         continue;
1423       }
1424
1425       if(isAdded) {
1426         *myDumpStorage << ", ";
1427       } else {
1428         isAdded = true;
1429       }
1430       *this << anAttribute;
1431     }
1432
1433     // check selection list is obtained by filters
1434     FiltersFeaturePtr aFilters = theAttrSelList->filters();
1435     if (aFilters) {
1436       if (theAttrSelList->size() > 0)
1437         *myDumpStorage << ", ";
1438       dumpFeature(aFilters, true);
1439     }
1440
1441     *myDumpStorage << "]";
1442   } else {
1443     // obtain name of list (the feature may contain several selection lists)
1444     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
1445     std::string aListName = name(anOwner) + "_objects";
1446     std::list<AttributePtr> aSelLists =
1447         anOwner->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
1448     if (aSelLists.size() > 1) {
1449       int anIndex = 1;
1450       for (std::list<AttributePtr>::iterator aSIt = aSelLists.begin();
1451            aSIt != aSelLists.end(); ++aSIt, ++anIndex)
1452         if ((*aSIt).get() == theAttrSelList.get())
1453           break;
1454       std::ostringstream aSStream;
1455       aSStream << aListName << "_" << anIndex;
1456       aListName = aSStream.str();
1457     }
1458     // reserve dumped buffer and store list "as is"
1459     myDumpStorage->reserveBuffer();
1460     aDumpAsIs = true;
1461     *this << aListName << " = " << theAttrSelList << "\n";
1462     aDumpAsIs = false;
1463     // append reserved data to the end of the current buffer
1464     myDumpStorage->restoreReservedBuffer();
1465     *myDumpStorage << aListName;
1466   }
1467   return *this;
1468 }
1469
1470 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1471   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
1472 {
1473   std::ostringstream aBuffer;
1474   aBuffer << "[";
1475   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
1476     if (anIndex != 0)
1477       aBuffer << ", ";
1478
1479     aBuffer << "\"" << theArray->value(anIndex) << "\"";
1480   }
1481   aBuffer << "]";
1482
1483   myDumpStorage->write(aBuffer.str());
1484   return *this;
1485 }
1486
1487 /// Dump std::endl
1488 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
1489                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
1490 {
1491   *theDumper.myDumpStorage << theEndl;
1492
1493   if (!theDumper.myEntitiesStack.empty()) {
1494     bool isCopy;
1495     // all copies have been stored into stack, pop them all
1496     do {
1497       isCopy = false;
1498       // Name for composite feature is dumped when all sub-entities are dumped
1499       // (see method ModelHighAPI_Dumper::processSubs).
1500       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
1501       CompositeFeaturePtr aComposite =
1502           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
1503       if (!aComposite) {
1504         theDumper.dumpEntitySetName();
1505         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
1506         if (aFeature) {
1507           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1508           isCopy = aCopyAttr.get() && aCopyAttr->value();
1509         }
1510       }
1511     } while (isCopy && !theDumper.myEntitiesStack.empty());
1512   }
1513
1514   // store all not-dumped entities first
1515   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
1516   theDumper.myDumpStorage->reserveBuffer();
1517   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
1518   for (; anIt != aNotDumped.end(); ++anIt) {
1519     // if the feature is composite, dump it with all subs
1520     CompositeFeaturePtr aCompFeat =
1521         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
1522     if (aCompFeat)
1523       theDumper.process(aCompFeat, true);
1524     else {
1525       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1526       theDumper.dumpFeature(aFeature, true);
1527       // dump the Projection feature which produces this "Copy" entity
1528       AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1529       if (aCopyAttr.get() && aCopyAttr->value())
1530       {
1531         const std::set<AttributePtr>& aRefs = aFeature->data()->refsToMe();
1532         std::set<AttributePtr>::iterator aRefIt = aRefs.begin();
1533         for (; aRefIt != aRefs.end(); ++aRefIt)
1534           if ((*aRefIt)->id() == "ProjectedFeature")
1535           { // process projection only
1536             FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
1537             if (anOwner && !theDumper.isDumped(EntityPtr(anOwner)))
1538               theDumper.dumpFeature(anOwner, true);
1539           }
1540       }
1541     }
1542   }
1543
1544   // then store the reserved data
1545   theDumper.myDumpStorage->restoreReservedBuffer();
1546   theDumper.myDumpStorage->mergeBuffer();
1547
1548   // now, store all postponed features
1549   theDumper.dumpPostponed();
1550
1551   return theDumper;
1552 }
1553
1554
1555 void ModelHighAPI_Dumper::exportVariables() const
1556 {
1557   DocumentPtr aRoot = ModelAPI_Session::get()->moduleDocument();
1558   EntityNameMap::const_iterator aNameIter = myNames.cbegin();
1559   for(; aNameIter != myNames.end(); aNameIter++) {
1560     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNameIter->first);
1561     if (aFeature.get() && aFeature->document() != aRoot) {
1562       FeaturePtr aPartFeat = ModelAPI_Tools::findPartFeature(aRoot, aFeature->document());
1563       if (aPartFeat.get()) {
1564         int aFeatureId = aFeature->data()->featureId();
1565         int aPartId = aPartFeat->data()->featureId();
1566         std::ostringstream anEntryStr;
1567         anEntryStr<<aPartId<<":"<<aFeatureId;
1568         std::string anEntry = anEntryStr.str();
1569         exportVariable(anEntry, aNameIter->second.myCurrentName);
1570       }
1571     }
1572   }
1573 }