Salome HOME
bos #29482 Export of colors and names to STEP.
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.cpp
index 993dc57b31f32854a26fa43d8f10d39d626d792c..a7a84cefd3de6c7760fb546e721702ce617d9058 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+// Copyright (C) 2014-2022  CEA/DEN, EDF R&D
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 
 #include <Config_PropManager.h>
 
-#include <GeomAPI_Pnt.h>
+#include <GeomAPI_Circ.h>
+#include <GeomAPI_Edge.h>
+#include <GeomAPI_Ellipse.h>
 #include <GeomAPI_Dir.h>
+#include <GeomAPI_Pnt.h>
+#include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_ShapeExplorer.h>
 #include <GeomAPI_ShapeIterator.h>
+#include <GeomAPI_Vertex.h>
 #include <GeomAlgoAPI_NExplode.h>
 
 #include <GeomDataAPI_Dir.h>
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <Locale_Convert.h>
 
 #include <ModelAPI_AttributeBoolean.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
 #include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeRefAttr.h>
 #include <ModelAPI_Document.h>
 #include <ModelAPI_Entity.h>
 #include <ModelAPI_Feature.h>
+#include <ModelAPI_FiltersFeature.h>
 #include <ModelAPI_Folder.h>
 #include <ModelAPI_Result.h>
 #include <ModelAPI_ResultBody.h>
 #include <ModelAPI_ResultConstruction.h>
+#include <ModelAPI_ResultGroup.h>
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_Session.h>
 #include <ModelAPI_Tools.h>
 
 #include <PartSetPlugin_Part.h>
 
-#include <OSD_OpenFile.hxx>
-
 #include <fstream>
+#include <iomanip>
+#include <cctype>
+#include <cmath>
+
+// ===========    Implementation of storage of dumped data    ===========
+static const int THE_DUMP_PRECISION = 16;
+
+class ModelHighAPI_Dumper::DumpStorageBuffer : public ModelHighAPI_Dumper::DumpStorage
+{
+public:
+  void addStorage(const ModelHighAPI_Dumper::DumpStoragePtr& theStorage)
+  { myStorageArray.push_back(theStorage); }
+
+  void clear() { myStorageArray.clear(); }
+
+  bool isBufferEmpty()
+  {
+    return myStorageArray.empty() || myStorageArray.front()->buffer().str().empty();
+  }
+
+  void mergeBuffer()
+  {
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt) {
+      // avoid multiple empty lines
+      std::string aBuf = (*anIt)->buffer().str();
+      size_t anInd = std::string::npos;
+      while ((anInd = aBuf.find("\n\n\n")) != std::string::npos)
+        aBuf.erase(anInd, 1);
+
+      (*anIt)->fullDump() << aBuf;
+      (*anIt)->buffer().str("");
+    }
+  }
+
+  void write(const std::string& theValue)
+  {
+    if (myStorageArray.empty())
+      addStorage(DumpStoragePtr(new DumpStorage));
+
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt)
+      (*anIt)->buffer() << theValue;
+  }
+
+  DumpStorageBuffer& operator<<(const char theChar)
+  {
+    std::ostringstream out;
+    out << theChar;
+    write(out.str());
+    return *this;
+  }
+
+  DumpStorageBuffer& operator<<(const char* theString)
+  {
+    write(theString);
+    return *this;
+  }
+
+  DumpStorageBuffer& operator<<(const std::string& theString)
+  {
+    write(theString);
+    return *this;
+  }
+
+  DumpStorageBuffer& operator<<(const bool theValue)
+  {
+    std::ostringstream out;
+    out << theValue;
+    write(out.str());
+    return *this;
+  }
+
+  DumpStorageBuffer& operator<<(const int theValue)
+  {
+    std::ostringstream out;
+    out << theValue;
+    write(out.str());
+    return *this;
+  }
+
+  DumpStorageBuffer& operator<<(const double theValue)
+  {
+    std::ostringstream out;
+    out << std::setprecision(THE_DUMP_PRECISION) << theValue;
+    write(out.str());
+    return *this;
+  }
+  /// Dump std::endl
+  friend
+  DumpStorageBuffer& operator<<(DumpStorageBuffer& theBuffer,
+                                std::basic_ostream<char>& (*)(std::basic_ostream<char>&))
+  {
+    theBuffer.write("\n");
+    return theBuffer;
+  }
+
+  void dumpArray(int theSize, double* theValues, std::string* theTexts)
+  {
+    std::ostringstream anOutput;
+    anOutput << std::setprecision(THE_DUMP_PRECISION);
+    for (int i = 0; i < theSize; ++i) {
+      if (i > 0)
+        anOutput << ", ";
+      if (theTexts[i].empty())
+        anOutput << theValues[i];
+      else
+        anOutput << "\"" << theTexts[i] << "\"";
+    }
+    write(anOutput.str());
+  }
+
+  virtual void write(const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
+  {
+    if (myStorageArray.empty())
+      addStorage(DumpStoragePtr(new DumpStorage));
+
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt)
+      (*anIt)->write(theAttrSelect);
+  }
+
+  virtual void reserveBuffer()
+  {
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt)
+      (*anIt)->reserveBuffer();
+  }
+
+  virtual void restoreReservedBuffer()
+  {
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt)
+      (*anIt)->restoreReservedBuffer();
+  }
+
+  virtual bool exportTo(const std::string& theFilename, const ModulesSet& theUsedModules)
+  {
+    static const std::string THE_EXT = ".py";
+    std::string aFilenameBase = theFilename;
+    if (aFilenameBase.rfind(THE_EXT) == aFilenameBase.size() - THE_EXT.size())
+      aFilenameBase = aFilenameBase.substr(0, aFilenameBase.size() - THE_EXT.size());
+
+    bool isOk = true;
+    std::list<ModelHighAPI_Dumper::DumpStoragePtr>::iterator anIt = myStorageArray.begin();
+    for (; anIt != myStorageArray.end(); ++anIt) {
+      std::string aFilename = aFilenameBase + (*anIt)->myFilenameSuffix + THE_EXT;
+      isOk = (*anIt)->exportTo(aFilename, theUsedModules) && isOk;
+    }
+    return isOk;
+  }
+
+private:
+  std::list<ModelHighAPI_Dumper::DumpStoragePtr> myStorageArray;
+};
+
+
+ModelHighAPI_Dumper::DumpStorage::DumpStorage(const DumpStorage& theOther)
+  : myFilenameSuffix(theOther.myFilenameSuffix),
+    myDumpBufferHideout(theOther.myDumpBufferHideout)
+{
+  myFullDump.str(theOther.myFullDump.str());
+  myDumpBuffer.str(theOther.myDumpBuffer.str());
+}
+
+const ModelHighAPI_Dumper::DumpStorage&
+ModelHighAPI_Dumper::DumpStorage::operator=(const ModelHighAPI_Dumper::DumpStorage& theOther)
+{
+  myFilenameSuffix = theOther.myFilenameSuffix;
+  myFullDump.str(theOther.myFullDump.str());
+  myDumpBuffer.str(theOther.myDumpBuffer.str());
+  myDumpBufferHideout = theOther.myDumpBufferHideout;
+  return *this;
+}
+
+void ModelHighAPI_Dumper::DumpStorage::reserveBuffer()
+{
+  myDumpBufferHideout.push(myDumpBuffer.str());
+  myDumpBuffer.str("");
+}
+
+void ModelHighAPI_Dumper::DumpStorage::restoreReservedBuffer()
+{
+  myDumpBuffer << myDumpBufferHideout.top();
+  myDumpBufferHideout.pop();
+}
+
+bool ModelHighAPI_Dumper::DumpStorage::exportTo(const std::string& theFilename,
+                                                const ModulesSet& theUsedModules)
+{
+  std::ofstream aFile(theFilename.c_str(), std::ofstream::out);
+  if (!aFile.is_open())
+    return false;
+
+  // standard header imported modules
+  for (ModulesSet::const_iterator aModIt = theUsedModules.begin();
+    aModIt != theUsedModules.end(); ++aModIt) {
+    aFile << "from " << *aModIt << " import *" << std::endl;
+  }
+  if (!theUsedModules.empty())
+    aFile << std::endl;
+
+  aFile << "from salome.shaper import model" << std::endl << std::endl;
+  aFile << "model.begin()" << std::endl;
+
+  // dump collected data
+  aFile << myFullDump.str();
+  aFile << myDumpBuffer.str();
+
+  // standard footer
+  aFile << "model.end()" << std::endl;
+  aFile.close();
+
+  return true;
+}
+
+static void getShapeAndContext(const AttributeSelectionPtr& theAttrSelect,
+                               GeomShapePtr& theShape, ResultPtr& theContext)
+{
+  if (theAttrSelect->isInitialized()) {
+    theShape = theAttrSelect->value();
+    theContext = theAttrSelect->context();
+    if (!theShape.get())
+      theShape = theContext->shape();
+
+    if (theAttrSelect->isGeometricalSelection() &&
+        theShape.get() && theShape->shapeType() == GeomAPI_Shape::COMPOUND &&
+        theContext.get() && !theShape->isEqual(theContext->shape()) &&
+        theContext->groupName() != ModelAPI_ResultPart::group() &&
+        theContext->groupName() != ModelAPI_ResultGroup::group()) {
+      GeomAPI_ShapeIterator anIt(theShape);
+      theShape = anIt.current();
+    }
+  }
+}
+
+void ModelHighAPI_Dumper::DumpStorage::write(const AttributeSelectionPtr& theAttrSelect)
+{
+  myDumpBuffer << "model.selection(";
+
+  GeomShapePtr aShape;
+  ResultPtr aContext;
+  getShapeAndContext(theAttrSelect, aShape, aContext);
+
+  if (aShape.get()) {
+    myDumpBuffer << "\"" << aShape->shapeTypeStr() << "\", \""
+                 << Locale::Convert::toString(theAttrSelect->namingName()) << "\"";
+  }
+
+  myDumpBuffer << ")";
+}
+
+static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
+                                     const ResultPtr& theResult,
+                                     const GeomShapePtr& theShape,
+                                     const FeaturePtr& theStartFeature,
+                                     const FeaturePtr& theEndFeature)
+{
+  DocumentPtr aDoc1 = theStartFeature->document();
+  DocumentPtr aDoc2 = theEndFeature->document();
+
+  std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
+  if (aDoc1 != aDoc2) {
+    // Find the position of the part, where its features should be inserted.
+    // It will avoid checking of appropriate elements in partSet after the current part.
+    std::list<FeaturePtr>::iterator aFIt = aFeatures.begin();
+    for (; aFIt != aFeatures.end(); ++aFIt) {
+      ResultPartPtr aPartRes =
+          std::dynamic_pointer_cast<ModelAPI_ResultPart>((*aFIt)->lastResult());
+      if (aPartRes && aPartRes->partDoc() == aDoc2)
+        break;
+    }
+
+    std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
+    aFeatures.insert(aFIt, anAdditionalFeatures.begin(), anAdditionalFeatures.end());
+  }
+
+  CompositeFeaturePtr aLastCompositeFeature;
+
+  std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
+  while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
+    CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
+    if (aCompFeat)
+      aLastCompositeFeature = aCompFeat;
+    ++aFIt;
+  }
+
+  // collect the list of composite features, containing the last feature;
+  // these features should be excluded from searching,
+  // because the feature cannot select sub-shapes from its parent
+  std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
+
+  int aNbPossibleSelections = 0;
+  for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
+    bool isSkipFeature = false;
+    if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
+      isSkipFeature = true;
+    CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
+    if (aCompFeat) {
+      ResultPartPtr aPartRes =
+          std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
+      if (!aPartRes)
+        aLastCompositeFeature = aCompFeat;
+      if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
+        // do not process the parent for the last feature,
+        // because it cannot select objects from its parent
+        isSkipFeature = true;
+      }
+    }
+    if (isSkipFeature)
+      continue;
+
+    std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
+    if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
+                                                 anApproproate)) {
+      bool isContinue = true;
+      std::list<std::pair<GeomShapePtr, int> > aCenters;
+      std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
+      for (; anApIt != anApproproate.end() && isContinue; ++anApIt) {
+        ++aNbPossibleSelections;
+
+        // stop if the target shape and result are found
+        GeomShapePtr aCurShape = anApIt->mySubshape;
+        if (!aCurShape)
+          aCurShape = anApIt->myResult->shape();
+
+        if (anApIt->myResult->isSame(theResult)) {
+          if (anApIt->myCenterType == (int)ModelAPI_AttributeSelection::NOT_CENTER)
+            isContinue = !aCurShape->isSame(theShape);
+          else if (theShape->isVertex() && aCurShape->isEdge()) {
+            GeomEdgePtr aCurEdge = aCurShape->edge();
+            GeomVertexPtr aVertex = theShape->vertex();
+            GeomPointPtr aCenter;
+            switch (anApIt->myCenterType) {
+            case (int)ModelAPI_AttributeSelection::CIRCLE_CENTER: {
+                GeomCirclePtr aCirc = aCurEdge->circle();
+                if (aCirc)
+                  aCenter = aCirc->center();
+                break;
+              }
+            case (int)ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS: {
+                GeomEllipsePtr anEllipse = aCurEdge->ellipse();
+                if (anEllipse)
+                  aCenter = anEllipse->firstFocus();
+                break;
+              }
+            case (int)ModelAPI_AttributeSelection::ELLIPSE_SECOND_FOCUS: {
+                GeomEllipsePtr anEllipse = aCurEdge->ellipse();
+                if (anEllipse)
+                  aCenter = anEllipse->secondFocus();
+                break;
+              }
+            }
+            if (aCenter && aCenter->distance(aVertex->point()) < 1.e-7)
+              aCenters.push_back(std::pair<GeomShapePtr, int>(aCurShape, aNbPossibleSelections));
+          }
+        }
+      }
+      // passed till the appropriate shape, check the center of circle
+      // or a focus of ellipse is selected
+      if (isContinue && !aCenters.empty())
+        aNbPossibleSelections = aCenters.front().second;
+    }
+  }
+  return aNbPossibleSelections;
+}
+
+void ModelHighAPI_Dumper::DumpStorageGeom::write(const AttributeSelectionPtr& theAttrSelect)
+{
+  GeomShapePtr aShape;
+  ResultPtr aContext;
+  getShapeAndContext(theAttrSelect, aShape, aContext);
+
+  // how to dump selection: construction features are dumped by name always
+  FeaturePtr aSelectedFeature;
+  FeaturePtr aFeature = theAttrSelect->contextFeature();
+  if (aShape && aContext && !aFeature)
+    aSelectedFeature = ModelAPI_Feature::feature(aContext->data()->owner());
+  bool isDumpByGeom = aSelectedFeature && aSelectedFeature->isInHistory();
+
+  if (isDumpByGeom) {
+    myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr();
+    // check the selected item is a ResultPart;
+    // in this case it is necessary to get shape with full transformation
+    // for correct calculation of the middle point
+    ResultPartPtr aResPart =
+      std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
+    if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
+      aShape = aResPart->shape();
+    GeomPointPtr aMiddlePoint = aShape->middlePoint();
+    // calculate number of features, which could be selected by the same point
+    FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
+    int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
+        theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
+
+    // produce the index if the number of applicable features is greater than 1
+    std::string anIndex;
+    if (aNbPossibleSelections > 1) {
+      std::ostringstream anOutput;
+      anOutput << "_" << aNbPossibleSelections;
+      anIndex = anOutput.str();
+    }
+
+    myDumpBuffer << std::setprecision(THE_DUMP_PRECISION)
+                 << anIndex << "\", ("
+                 << aMiddlePoint->x() << ", "
+                 << aMiddlePoint->y() << ", "
+                 << aMiddlePoint->z() << ")";
+    myDumpBuffer << ")";
+  }
+  else
+    DumpStorage::write(theAttrSelect);
+}
+
+void ModelHighAPI_Dumper::DumpStorageWeak::write(const AttributeSelectionPtr& theAttrSelect)
+{
+  GeomShapePtr aShape;
+  ResultPtr aContext;
+  getShapeAndContext(theAttrSelect, aShape, aContext);
+
+  bool aStandardDump = true;
+  if (aShape.get() && aContext.get() &&
+      aShape != aContext->shape()) { // weak naming for local selection only
+    GeomAlgoAPI_NExplode aNExplode(aContext->shape(), aShape->shapeType());
+    int anIndex = aNExplode.index(aShape);
+    if (anIndex != 0) { // found a week-naming index, so, export it
+      myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr() << "\", \""
+                   << Locale::Convert::toString(theAttrSelect->contextName(aContext))
+                   << "\", " << anIndex << ")";
+      aStandardDump = false;
+    }
+  }
+  if (aStandardDump)
+    DumpStorage::write(theAttrSelect);
+}
+// ======================================================================
+
 
 static int gCompositeStackDepth = 0;
 
 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
 
 ModelHighAPI_Dumper::ModelHighAPI_Dumper()
-  : myGeometricalSelection(false)
+  : myDumpStorage(new DumpStorageBuffer),
+    myDumpPostponedInProgress(false)
+{
+}
+
+ModelHighAPI_Dumper::~ModelHighAPI_Dumper()
 {
-  clear();
+  delete myDumpStorage;
 }
 
 void ModelHighAPI_Dumper::setInstance(ModelHighAPI_Dumper* theDumper)
@@ -84,26 +535,22 @@ ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
   return mySelf;
 }
 
-void ModelHighAPI_Dumper::clear(bool bufferOnly)
+void ModelHighAPI_Dumper::addCustomStorage(const ModelHighAPI_Dumper::DumpStoragePtr& theStorage)
 {
-  myDumpBuffer.str("");
-  myDumpBuffer << std::setprecision(16);
-
-  clearNotDumped();
-
-  if (!bufferOnly) {
-    myFullDump.str("");
-    myFullDump << std::setprecision(16);
+  myDumpStorage->addStorage(theStorage);
+}
 
-    myNames.clear();
-    myModules.clear();
-    myFeatureCount.clear();
-    while (!myEntitiesStack.empty())
-      myEntitiesStack.pop();
+void ModelHighAPI_Dumper::clearCustomStorage()
+{
+  myDumpStorage->clear();
 
-    myPostponed.clear();
-    myDumpPostponedInProgress = false;
-  }
+  myNames.clear();
+  myModules.clear();
+  myFeatureCount.clear();
+  myPostponed.clear();
+  while (!myEntitiesStack.empty())
+    myEntitiesStack.pop();
+  clearNotDumped();
 }
 
 void ModelHighAPI_Dumper::clearNotDumped()
@@ -125,25 +572,30 @@ static int toInt(const std::string& theString)
 
 const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
                                              bool theSaveNotDumped,
-                                             bool theUseEntityName)
+                                             bool theUseEntityName,
+                                             bool theSetIsDumped)
 {
-  EntityNameMap::const_iterator aFound = myNames.find(theEntity);
-  if (aFound != myNames.end())
+  EntityNameMap::iterator aFound = myNames.find(theEntity);
+  if (aFound != myNames.end()) {
+    // Set dumped flag for postponed constraints which are without names
+    if (!aFound->second.myIsDumped)
+      aFound->second.myIsDumped = theSetIsDumped;
     return aFound->second.myCurrentName;
-
+  }
   // entity is not found, store it
-  std::string aName, aKind;
+  std::string aName;
+  std::string aKind;
   bool isDefaultName = false;
   bool isSaveNotDumped = theSaveNotDumped;
   std::ostringstream aDefaultName;
   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
   if (aFeature) {
-    aName = aFeature->name();
+    aName = Locale::Convert::toString(aFeature->name());
     aKind = aFeature->getKind();
   } else {
     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(theEntity);
     if (aFolder) {
-      aName = aFolder->data()->name();
+      aName = Locale::Convert::toString(aFolder->data()->name());
       aKind = ModelAPI_Folder::ID();
       isSaveNotDumped = false;
     }
@@ -189,10 +641,10 @@ const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
       int aFullIndex = 0;
       NbFeaturesMap::const_iterator aFIt = myFeatureCount.begin();
       for (; aFIt != myFeatureCount.end(); ++aFIt) {
-        std::map<std::string, std::pair<int, int> >::const_iterator aFound =
+        std::map<std::string, std::pair<int, int> >::const_iterator aFoundKind =
           aFIt->second.find(aKind);
-        if (aFound != aFIt->second.end())
-          aFullIndex += aFound->second.first;
+        if (aFoundKind != aFIt->second.end())
+          aFullIndex += aFoundKind->second.first;
       }
       aDefaultName << aKind << "_" << aFullIndex;
     }
@@ -206,6 +658,8 @@ const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
   if (aFeature)
     saveResultNames(aFeature);
 
+  myNames[theEntity].myIsDumped = theSetIsDumped;
+
   return myNames[theEntity].myCurrentName;
 }
 
@@ -230,13 +684,12 @@ void ModelHighAPI_Dumper::saveResultNames(const FeaturePtr& theFeature)
   bool isFeatureDefaultName = myNames[theFeature].myIsDefault;
 
   // Save only names of results which is not correspond to default feature name
-  const std::list<ResultPtr>& aResults = theFeature->results();
   std::list<ResultPtr> allRes;
   ModelAPI_Tools::allResults(theFeature, allRes);
   for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
-    std::pair<std::string, bool> aName = ModelAPI_Tools::getDefaultName(*aRes);
-    std::string aDefaultName = aName.first;
-    std::string aResName = (*aRes)->data()->name();
+    std::pair<std::wstring, bool> aName = ModelAPI_Tools::getDefaultName(*aRes, true, true);
+    std::string aDefaultName = Locale::Convert::toString(aName.first);
+    std::string aResName = Locale::Convert::toString((*aRes)->data()->name());
     bool isUserDefined = !(isFeatureDefaultName && aDefaultName == aResName);
     myNames[*aRes] =
       EntityName(aResName, (isUserDefined ? aResName : std::string()), !isUserDefined);
@@ -252,7 +705,8 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theD
   *this << aDocName << " = model.moduleDocument()" << std::endl;
 
   // dump subfeatures and store result to file
-  return process(theDoc) && exportTo(theFileName);
+  bool isOk = process(theDoc) && myDumpStorage->exportTo(theFileName, myModules);
+  return isOk;
 }
 
 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
@@ -269,8 +723,12 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theD
   // dump all other features
   for (anObjIt = anObjects.begin(); anObjIt != anObjects.end(); ++anObjIt) {
     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anObjIt);
-    if (aCompFeat) // iteratively process composite features
-      isOk = process(aCompFeat) && isOk;
+    if (aCompFeat) {
+      // iteratively process composite features,
+      // if the composite feature is the last in the document, no need to dump "model.do()" action
+      std::list<ObjectPtr>::const_iterator aNext = anObjIt;
+      isOk = process(aCompFeat, false, ++aNext != anObjects.end()) && isOk;
+    }
     else if (!isDumped(EntityPtr(*anObjIt))) {
       // dump folder
       FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anObjIt);
@@ -289,7 +747,7 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theD
 }
 
 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
-                                  bool isForce)
+                                  bool isForce, bool isDumpModelDo)
 {
   // increase composite features stack
   ++gCompositeStackDepth;
@@ -322,7 +780,9 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeatur
     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
     // dump features in the document
     bool aRes = process(aSubDoc);
-    *this << "model.do()" << std::endl;
+    if (isDumpModelDo)
+      *this << "\nmodel.do()\n";
+    *this << std::endl;
     return aRes;
   }
 
@@ -413,14 +873,15 @@ void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFea
                                                      const FeaturePtr& theSubFeature)
 {
   name(theSubFeature, false);
-  myNames[theSubFeature] = EntityName(theSubFeatureGet, theSubFeature->name(), false);
+  myNames[theSubFeature] =
+    EntityName(theSubFeatureGet, Locale::Convert::toString(theSubFeature->name()), false);
 
   // store results if they have user-defined names or colors
   std::list<ResultPtr> aResultsWithNameOrColor;
   const std::list<ResultPtr>& aResults = theSubFeature->results();
   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
   for (; aResIt != aResults.end(); ++aResIt) {
-    std::string aResName = (*aResIt)->data()->name();
+    std::string aResName = Locale::Convert::toString((*aResIt)->data()->name());
     myNames[*aResIt] = EntityName(aResName, aResName, false);
     aResultsWithNameOrColor.push_back(*aResIt);
   }
@@ -431,38 +892,6 @@ void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFea
   dumpEntitySetName();
 }
 
-bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
-{
-  std::ofstream aFile;
-  OSD_OpenStream(aFile, theFileName.c_str(), std::ofstream::out);
-  if (!aFile.is_open())
-    return false;
-
-  // standard header (encoding + imported modules)
-  aFile << "# -*- coding: utf-8 -*-" << std::endl << std::endl;
-  for (ModulesSet::const_iterator aModIt = myModules.begin();
-       aModIt != myModules.end(); ++aModIt) {
-    aFile << "from " << *aModIt << " import *" << std::endl;
-  }
-  if (!myModules.empty())
-    aFile << std::endl;
-
-  aFile << "from salome.shaper import model" << std::endl << std::endl;
-  aFile << "model.begin()" << std::endl;
-
-  // dump collected data
-  aFile << myFullDump.str();
-  aFile << myDumpBuffer.str();
-
-  // standard footer
-  aFile << "model.end()" << std::endl;
-
-  aFile.close();
-  clear();
-
-  return true;
-}
-
 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName)
 {
   myModules.insert(theModuleName);
@@ -471,14 +900,14 @@ void ModelHighAPI_Dumper::importModule(const std::string& theModuleName)
 void ModelHighAPI_Dumper::dumpEntitySetName()
 {
   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
-  bool isBufferEmpty = myDumpBuffer.str().empty();
+  bool isBufferEmpty = myDumpStorage->isBufferEmpty();
 
   // dump "setName" for the entity
   if (aLastDumped.myUserName) {
     EntityName& anEntityNames = myNames[aLastDumped.myEntity];
     if (!anEntityNames.myIsDefault)
-      myDumpBuffer << anEntityNames.myCurrentName << ".setName(\""
-                   << anEntityNames.myUserName << "\")" << std::endl;
+      *myDumpStorage << anEntityNames.myCurrentName << ".setName(\""
+                     << anEntityNames.myUserName << "\")\n";
     // don't dump "setName" for the entity twice
     anEntityNames.myUserName.clear();
     anEntityNames.myIsDefault = true;
@@ -491,7 +920,7 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
     EntityName& anEntityNames = myNames[*aResIt];
     if (!anEntityNames.myIsDefault) {
       *this << *aResIt;
-      myDumpBuffer << ".setName(\"" << anEntityNames.myUserName << "\")" << std::endl;
+      *myDumpStorage << ".setName(\"" << anEntityNames.myUserName << "\")\n";
       // don't dump "setName" for the entity twice
       anEntityNames.myUserName.clear();
       anEntityNames.myIsDefault = true;
@@ -501,8 +930,8 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
       AttributeIntArrayPtr aColor = (*aResIt)->data()->intArray(ModelAPI_Result::COLOR_ID());
       if (aColor && aColor->isInitialized()) {
         *this << *aResIt;
-        myDumpBuffer << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
-                     << ", " << aColor->value(2) << ")" << std::endl;
+        *myDumpStorage << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
+                       << ", " << aColor->value(2) << ")\n";
       }
     }
     // set result deflection
@@ -511,7 +940,7 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
         (*aResIt)->data()->real(ModelAPI_Result::DEFLECTION_ID());
       if(aDeflectionAttr.get() && aDeflectionAttr->isInitialized()) {
         *this << *aResIt;
-        myDumpBuffer << ".setDeflection(" << aDeflectionAttr->value() << ")" << std::endl;
+        *myDumpStorage << ".setDeflection(" << aDeflectionAttr->value() << ")\n";
       }
     }
     // set result transparency
@@ -520,7 +949,7 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
         (*aResIt)->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
       if(aTransparencyAttr.get() && aTransparencyAttr->isInitialized()) {
         *this << *aResIt;
-        myDumpBuffer << ".setTransparency(" << aTransparencyAttr->value() << ")" << std::endl;
+        *myDumpStorage << ".setTransparency(" << aTransparencyAttr->value() << ")\n";
       }
     }
   }
@@ -529,10 +958,8 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
   myEntitiesStack.pop();
 
   // clean buffer if it was clear before
-  if (isBufferEmpty) {
-    myFullDump << myDumpBuffer.str();
-    myDumpBuffer.str("");
-  }
+  if (isBufferEmpty)
+    myDumpStorage->mergeBuffer();
 }
 
 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
@@ -565,6 +992,18 @@ bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
   return true;
 }
 
+size_t ModelHighAPI_Dumper::indexOfFirstNotDumped(
+    const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList) const
+{
+  size_t anIndex = 0;
+  std::list<ObjectPtr> anObjects = theRefList->list();
+  for (std::list<ObjectPtr>::const_iterator anIt = anObjects.begin();
+       anIt != anObjects.end(); ++anIt, ++anIndex)
+    if (!isDumped(ModelAPI_Feature::feature(*anIt)))
+      break;
+  return anIndex;
+}
+
 static bool isSketchSub(const FeaturePtr& theFeature)
 {
   static const std::string SKETCH("Sketch");
@@ -637,85 +1076,92 @@ bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) cons
   return fabs(anAttribute->value()) < 1.e-12;
 }
 
+bool ModelHighAPI_Dumper::dumpCommentBeforeFeature(const FeaturePtr& theFeature) const
+{
+  // currently, the comment should not be dumped only before the filters
+  FiltersFeaturePtr aFilters = std::dynamic_pointer_cast<ModelAPI_FiltersFeature>(theFeature);
+  if (aFilters)
+    return false;
+  // all other features should be commented before the dump
+  return !isDumped(theFeature);
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
 {
-  myDumpBuffer << theChar;
+  *myDumpStorage << theChar;
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
 {
-  myDumpBuffer << theString;
+  *myDumpStorage << theString;
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
 {
-  myDumpBuffer << theString;
+  *myDumpStorage << theString;
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::wstring& theString)
+{
+  *myDumpStorage << Locale::Convert::toString(theString);
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
 {
-  myDumpBuffer << (theValue ? "True" : "False");
+  *myDumpStorage << (theValue ? "True" : "False");
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
 {
-  myDumpBuffer << theValue;
+  *myDumpStorage << theValue;
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
 {
-  myDumpBuffer << theValue;
+  *myDumpStorage << theValue;
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
 {
   importModule("GeomAPI");
-  myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
-               << thePoint->y() << ", " << thePoint->z() << ")";
+  *myDumpStorage << "GeomAPI_Pnt(" << thePoint->x() << ", "
+                 << thePoint->y() << ", " << thePoint->z() << ")";
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
 {
   importModule("GeomAPI");
-  myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
-               << theDir->y() << ", " << theDir->z() << ")";
+  *myDumpStorage << "GeomAPI_Dir(" << theDir->x() << ", "
+                 << theDir->y() << ", " << theDir->z() << ")";
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
 {
-  myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
+  *myDumpStorage << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
   return *this;
 }
 
-static void dumpArray(std::ostringstream& theOutput, int theSize,
-                      double* theValues, std::string* theTexts)
-{
-  for (int i = 0; i < theSize; ++i) {
-    if (i > 0)
-      theOutput << ", ";
-    if (theTexts[i].empty())
-      theOutput << theValues[i];
-    else
-      theOutput << "\"" << theTexts[i] << "\"";
-  }
-}
-
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
 {
   static const int aSize = 3;
   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
-  std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
-  dumpArray(myDumpBuffer, aSize, aValues, aTexts);
+  std::string aTexts[aSize] = {
+      Locale::Convert::toString(thePoint->textX()),
+      Locale::Convert::toString(thePoint->textY()),
+      Locale::Convert::toString(thePoint->textZ())
+  };
+  myDumpStorage->dumpArray(aSize, aValues, aTexts);
   return *this;
 }
 
@@ -724,50 +1170,137 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
 {
   static const int aSize = 2;
   double aValues[aSize] = {thePoint->x(), thePoint->y()};
-  std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
-  dumpArray(myDumpBuffer, aSize, aValues, aTexts);
+  std::string aTexts[aSize] = {
+      Locale::Convert::toString(thePoint->textX()),
+      Locale::Convert::toString(thePoint->textY())
+  };
+  myDumpStorage->dumpArray(aSize, aValues, aTexts);
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+  const std::shared_ptr<GeomDataAPI_Point2DArray>& thePointArray)
+{
+  static const int aThreshold = 4;
+  static bool aDumpAsIs = false;
+  static std::string aSeparator = "";
+  // if number of elements in the list if greater than a threshold,
+  // dump it in a separate line with specific name
+  int aSize = thePointArray->size();
+  if (aDumpAsIs || aSize <= aThreshold) {
+    *myDumpStorage << "[";
+    GeomPnt2dPtr aPoint = thePointArray->pnt(0);
+    *myDumpStorage << "(" << aPoint->x() << ", " << aPoint->y() << ")";
+    for (int anIndex = 1; anIndex < aSize; ++anIndex) {
+      aPoint = thePointArray->pnt(anIndex);
+      *myDumpStorage << "," << aSeparator << " (" << aPoint->x() << ", " << aPoint->y() << ")";
+    }
+    *myDumpStorage << aSeparator << "]";
+  }
+  else {
+    // name of list
+    FeaturePtr anOwner = ModelAPI_Feature::feature(thePointArray->owner());
+    std::string aListName = name(anOwner) + "_" + thePointArray->id();
+    // reserve dumped buffer and store list "as is"
+    myDumpStorage->reserveBuffer();
+    aDumpAsIs = true;
+    aSeparator = std::string("\n") + std::string(aListName.size() + 3, ' ');
+    *this << aListName << " = " << thePointArray << "\n";
+    aDumpAsIs = false;
+    aSeparator = "";
+    // append reserved data to the end of the current buffer
+    myDumpStorage->restoreReservedBuffer();
+    *myDumpStorage << aListName;
+  }
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
 {
-  myDumpBuffer << (theAttrBool->value() ? "True" : "False");
+  *myDumpStorage << (theAttrBool->value() ? "True" : "False");
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
 {
-  std::string aText = theAttrInt->text();
+  std::string aText = Locale::Convert::toString(theAttrInt->text());
   if (aText.empty())
-    myDumpBuffer << theAttrInt->value();
+    *myDumpStorage << theAttrInt->value();
   else
-    myDumpBuffer << "\"" << aText << "\"";
+    *myDumpStorage << "\"" << aText << "\"";
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeIntArray>& theArray)
+{
+  *myDumpStorage << "[";
+  int aSize = theArray->size();
+  if (aSize > 0) {
+    *myDumpStorage << theArray->value(0);
+    for (int anIndex = 1; anIndex < aSize; ++anIndex)
+      *myDumpStorage << ", " << theArray->value(anIndex);
+  }
+  *myDumpStorage << "]";
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
 {
-  std::string aText = theAttrReal->text();
+  std::string aText = Locale::Convert::toString(theAttrReal->text());
   if (aText.empty())
-    myDumpBuffer << theAttrReal->value();
+    *myDumpStorage << theAttrReal->value();
   else
-    myDumpBuffer << "\"" << aText << "\"";
+    *myDumpStorage << "\"" << aText << "\"";
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+  const std::shared_ptr<ModelAPI_AttributeDoubleArray>& theArray)
+{
+  *myDumpStorage << "[";
+  int aSize = theArray->size();
+  if (aSize > 0) {
+    *myDumpStorage << theArray->value(0);
+    for (int anIndex = 1; anIndex < aSize; ++anIndex)
+      *myDumpStorage << ", " << theArray->value(anIndex);
+  }
+  *myDumpStorage << "]";
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
 {
-  myDumpBuffer << "\"" << theAttrStr->value() << "\"";
+  // escaping the quote signs in the string under dumping
+  std::string aStr = theAttrStr->value();
+  /*
+  size_t aPos = aStr.find("\"");
+  while (aPos != std::string::npos) {
+    aStr.insert(aPos, "\\");
+    aPos = aStr.find("\"", aPos + 2);
+  }
+  aPos = aStr.find("\'");
+  while (aPos != std::string::npos) {
+    aStr.insert(aPos, "\\");
+    aPos = aStr.find("\'", aPos + 2);
+  }
+  */
+  size_t aPos = aStr.find_first_of("\"\'");
+  while (aPos != std::string::npos) {
+    aStr.insert(aPos, "\\");
+    aPos = aStr.find_first_of("\"\'", aPos + 2);
+  }
+  *myDumpStorage << "'" << aStr << "'";
   return *this;
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
 {
-  myDumpBuffer << name(theFolder);
+  *myDumpStorage << name(theFolder);
 
   // add dumped folder to a stack
   if (!myNames[theFolder].myIsDumped &&
@@ -779,7 +1312,7 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
 {
-  myDumpBuffer << name(theEntity);
+  *myDumpStorage << name(theEntity);
 
   if (!myNames[theEntity].myIsDumped) {
     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
@@ -825,28 +1358,41 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
     aCurRes = aParent;
   }
 
-  myDumpBuffer << name(aFeature);
+  *myDumpStorage << name(aFeature);
   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
     if (anI == anIndices.begin()) {
       if(*anI == 0) {
-        myDumpBuffer << ".result()";
+        *myDumpStorage << ".result()";
       }
       else {
-        myDumpBuffer << ".results()[" << *anI << "]";
+        *myDumpStorage << ".results()[" << *anI << "]";
       }
     } else {
-      myDumpBuffer << ".subResult(" << *anI << ")";
+      *myDumpStorage << ".subResult(" << *anI << ")";
     }
   }
 
   return *this;
 }
 
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::list<ResultPtr>& theResults)
+{
+  *this << "[";
+  for (std::list<ResultPtr>::const_iterator anIt = theResults.begin();
+       anIt != theResults.end(); ++anIt) {
+    if (anIt != theResults.begin())
+      *this << ", ";
+    *this << *anIt;
+  }
+  *this << "]";
+  return *this;
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
 {
   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
   if(aFeature.get()) {
-    myDumpBuffer << name(aFeature);
+    *myDumpStorage << name(aFeature);
     return *this;
   }
 
@@ -867,14 +1413,15 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr
   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
   // In this case we need to cast explicitly feature to appropriate type.
   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
-  if (isCopy.get() && isCopy->value()) {
+  AttributeReferencePtr hasParent = anOwner->reference("ParentFeature");
+  if ((isCopy.get() && isCopy->value()) || (hasParent && hasParent->value())) {
     aWrapperPrefix = featureWrapper(anOwner) + "(";
     aWrapperSuffix = ")";
     importModule("SketchAPI");
   }
 
-  myDumpBuffer << aWrapperPrefix << name(anOwner) << aWrapperSuffix
-               << "." << attributeGetter(anOwner, theAttr->id()) << "()";
+  *myDumpStorage << aWrapperPrefix << name(anOwner) << aWrapperSuffix
+                 << "." << attributeGetter(anOwner, theAttr->id()) << "()";
   return *this;
 }
 
@@ -891,13 +1438,13 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
 {
-  myDumpBuffer << "[";
+  *myDumpStorage << "[";
   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
   bool isAdded = false;
   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
   for (; anIt != aList.end(); ++anIt) {
     if (isAdded)
-      myDumpBuffer << ", ";
+      *myDumpStorage << ", ";
     else
       isAdded = true;
     if (anIt->first)
@@ -905,7 +1452,7 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     else if (anIt->second)
       * this << anIt->second;
   }
-  myDumpBuffer << "]";
+  *myDumpStorage << "]";
   return *this;
 }
 
@@ -920,190 +1467,48 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
 {
   static const int aThreshold = 2;
+  static bool aDumpAsIs = false;
   // if number of elements in the list if greater than a threshold,
   // dump it in a separate line with specific name
-  std::string aDumped = myDumpBuffer.str();
-  if (aDumped.empty() || theRefList->size() <= aThreshold) {
-    myDumpBuffer << "[";
+  if (aDumpAsIs || theRefList->size() <= aThreshold) {
+    *myDumpStorage << "[";
     std::list<ObjectPtr> aList = theRefList->list();
     bool isAdded = false;
     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
     for (; anIt != aList.end(); ++anIt) {
+      if (!(*anIt))
+        continue;
+      if (!isDumped(ModelAPI_Feature::feature(*anIt)))
+        break; // stop if the object is not dumped yet (parent feature should be postponed)
+
       if (isAdded)
-        myDumpBuffer << ", ";
+        *myDumpStorage << ", ";
       else
         isAdded = true;
 
       *this << *anIt;
     }
-    myDumpBuffer << "]";
+    *myDumpStorage << "]";
   } else {
-    // clear buffer and store list "as is"
-    myDumpBuffer.str("");
-    *this << theRefList;
-    // save buffer and clear it again
-    std::string aDumpedList = myDumpBuffer.str();
-    myDumpBuffer.str("");
-    // obtain name of list
+    // name of list
     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
     std::string aListName = name(anOwner) + "_objects";
-    // store all previous data
-    myDumpBuffer << aListName << " = " << aDumpedList << std::endl
-                 << aDumped << aListName;
+    // reserve dumped buffer and store list "as is"
+    myDumpStorage->reserveBuffer();
+    aDumpAsIs = true;
+    *this << aListName << " = " << theRefList << "\n";
+    aDumpAsIs = false;
+    // append reserved data to the end of the current buffer
+    myDumpStorage->restoreReservedBuffer();
+    *myDumpStorage << aListName;
   }
   return *this;
 }
 
-static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
-                                     const ResultPtr& theResult,
-                                     const GeomShapePtr& theShape,
-                                     const FeaturePtr& theStartFeature,
-                                     const FeaturePtr& theEndFeature)
-{
-  DocumentPtr aDoc1 = theStartFeature->document();
-  DocumentPtr aDoc2 = theEndFeature->document();
-
-  std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
-  if (aDoc1 != aDoc2) {
-    std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
-    aFeatures.insert(aFeatures.end(), anAdditionalFeatures.begin(), anAdditionalFeatures.end());
-  }
-
-  CompositeFeaturePtr aLastCompositeFeature;
-
-  std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
-  while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
-    CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
-    if (aCompFeat)
-      aLastCompositeFeature = aCompFeat;
-    ++aFIt;
-  }
-
-  // collect the list of composite features, containing the last feature;
-  // these features should be excluded from searching,
-  // because the feature cannot select sub-shapes from its parent
-  std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
-
-  int aNbPossibleSelections = 0;
-  for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
-    bool isSkipFeature = false;
-    if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
-      isSkipFeature = true;
-    CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
-    if (aCompFeat) {
-      ResultPartPtr aPartRes =
-          std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
-      if (!aPartRes)
-        aLastCompositeFeature = aCompFeat;
-      if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
-        // do not process the parent for the last feature,
-        // because it cannot select objects from its parent
-        isSkipFeature = true;
-      }
-    }
-    if (isSkipFeature)
-      continue;
-
-    std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
-    if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
-                                                 anApproproate)) {
-      std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
-      for (; anApIt != anApproproate.end(); ++anApIt) {
-        ++aNbPossibleSelections;
-
-        // stop if the target shape and result are found
-        GeomShapePtr aCurShape = anApIt->mySubshape;
-        if (!aCurShape)
-          aCurShape = anApIt->myResult->shape();
-
-        if (anApIt->myResult->isSame(theResult) && aCurShape->isSame(theShape))
-          break;
-      }
-    }
-  }
-  return aNbPossibleSelections;
-}
-
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
 {
-  myDumpBuffer << "model.selection(";
-
-  if(!theAttrSelect->isInitialized()) {
-    myDumpBuffer << ")";
-    return *this;
-  }
-
-  GeomShapePtr aShape = theAttrSelect->value();
-  if(!aShape.get()) {
-    aShape = theAttrSelect->context()->shape();
-  }
-
-  if(!aShape.get()) {
-    myDumpBuffer << ")";
-    return *this;
-  }
-
-  // how to dump selection: construction features are dumped by name always
-  bool isDumpByGeom = myGeometricalSelection;
-  FeaturePtr aSelectedFeature;
-  if (isDumpByGeom) {
-    ResultPtr aRes = theAttrSelect->context();
-    FeaturePtr aFeature = theAttrSelect->contextFeature();
-    if (aRes && !aFeature)
-      aSelectedFeature = ModelAPI_Feature::feature(aRes->data()->owner());
-    isDumpByGeom = aSelectedFeature && aSelectedFeature->isInHistory();
-  }
-
-  if (theAttrSelect->isGeometricalSelection() && aShape->shapeType() == GeomAPI_Shape::COMPOUND
-    && theAttrSelect->context().get() && !aShape->isEqual(theAttrSelect->context()->shape())
-    && theAttrSelect->context()->groupName() != ModelAPI_ResultPart::group()) {
-    GeomAPI_ShapeIterator anIt(aShape);
-    aShape = anIt.current();
-  }
-
-  myDumpBuffer << "\"" << aShape->shapeTypeStr();
-  bool aStandardDump = true;
-  if (isDumpByGeom) {
-    // check the selected item is a ResultPart;
-    // in this case it is necessary to get shape with full transformation
-    // for correct calculation of the middle point
-    ResultPartPtr aResPart =
-        std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
-    if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
-      aShape = aResPart->shape();
-    GeomPointPtr aMiddlePoint = aShape->middlePoint();
-    // calculate number of features, which could be selected by the same point
-    FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
-    int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
-        theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
-
-    // produce the index if the number of applicable features is greater than 1
-    std::string anIndex;
-    if (aNbPossibleSelections > 1) {
-      std::ostringstream anOutput;
-      anOutput << "_" << aNbPossibleSelections;
-      anIndex = anOutput.str();
-    }
-
-    myDumpBuffer << anIndex << "\", ("
-                 << aMiddlePoint->x() << ", "
-                 << aMiddlePoint->y() << ", "
-                 << aMiddlePoint->z() << ")";
-    aStandardDump = false;
-  } else if (myWeakNamingSelection && aShape.get() && theAttrSelect->context().get() &&
-       aShape != theAttrSelect->context()->shape()) { // weak naming for local selection only
-    GeomAlgoAPI_NExplode aNExplode(theAttrSelect->context()->shape(), aShape->shapeType());
-    int anIndex = aNExplode.index(aShape);
-    if (anIndex != 0) { // found a week-naming index, so, export it
-      myDumpBuffer<<"\", \""<<
-        theAttrSelect->contextName(theAttrSelect->context())<<"\", "<<anIndex;
-      aStandardDump = false;
-    }
-  }
-  if (aStandardDump)
-    myDumpBuffer << "\", \"" << theAttrSelect->namingName() << "\"";
-  myDumpBuffer << ")";
+  myDumpStorage->write(theAttrSelect);
   return *this;
 }
 
@@ -1111,12 +1516,11 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
 {
   static const int aThreshold = 2;
+  static int aNbSpaces = 0;
   // if number of elements in the list if greater than a threshold,
   // dump it in a separate line with specific name
-  std::string aDumped = myDumpBuffer.str();
-
-  if (aDumped.empty() || theAttrSelList->size() <= aThreshold) {
-    myDumpBuffer << "[";
+  if (aNbSpaces > 0 || theAttrSelList->size() <= aThreshold) {
+    *myDumpStorage << "[";
 
     GeomShapePtr aShape;
     std::string aShapeTypeStr;
@@ -1137,21 +1541,28 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
       }
 
       if(isAdded) {
-        myDumpBuffer << ", ";
+        // print each attribute on separate line with the appropriate shift
+        if (aNbSpaces > 0) {
+          std::string aSpaces(aNbSpaces + 1, ' ');
+          *myDumpStorage << ",\n" << aSpaces;
+        } else
+          *myDumpStorage << ", ";
       } else {
         isAdded = true;
       }
       *this << anAttribute;
     }
 
-    myDumpBuffer << "]";
+    // check selection list is obtained by filters
+    FiltersFeaturePtr aFilters = theAttrSelList->filters();
+    if (aFilters) {
+      if (theAttrSelList->size() > 0)
+        *myDumpStorage << ", ";
+      dumpFeature(aFilters, true);
+    }
+
+    *myDumpStorage << "]";
   } else {
-    // clear buffer and store list "as is"
-    myDumpBuffer.str("");
-    *this << theAttrSelList;
-    // save buffer and clear it again
-    std::string aDumpedList = myDumpBuffer.str();
-    myDumpBuffer.str("");
     // obtain name of list (the feature may contain several selection lists)
     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
     std::string aListName = name(anOwner) + "_objects";
@@ -1167,9 +1578,14 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
       aSStream << aListName << "_" << anIndex;
       aListName = aSStream.str();
     }
-    // store all previous data
-    myDumpBuffer << aListName << " = " << aDumpedList << std::endl
-                 << aDumped << aListName;
+    // reserve dumped buffer and store list "as is"
+    myDumpStorage->reserveBuffer();
+    aNbSpaces = (int)aListName.size() + 3;
+    *this << aListName << " = " << theAttrSelList << "\n";
+    aNbSpaces = 0;
+    // append reserved data to the end of the current buffer
+    myDumpStorage->restoreReservedBuffer();
+    *myDumpStorage << aListName;
   }
   return *this;
 }
@@ -1177,23 +1593,30 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
 {
-  myDumpBuffer<<"[";
+  std::ostringstream aBuffer;
+  aBuffer << "[";
   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
     if (anIndex != 0)
-      myDumpBuffer<<", ";
+      aBuffer << ", ";
 
-    myDumpBuffer<<"\""<<theArray->value(anIndex)<<"\"";
+    aBuffer << "\"" << theArray->value(anIndex) << "\"";
   }
+  aBuffer << "]";
 
-  myDumpBuffer<<"]";
+  myDumpStorage->write(aBuffer.str());
   return *this;
 }
 
+void ModelHighAPI_Dumper::newline()
+{
+  *this << std::endl;
+}
+
 /// Dump std::endl
 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
 {
-  theDumper.myDumpBuffer << theEndl;
+  *theDumper.myDumpStorage << theEndl;
 
   if (!theDumper.myEntitiesStack.empty()) {
     bool isCopy;
@@ -1218,8 +1641,8 @@ ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
 
   // store all not-dumped entities first
   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
-  std::string aBufCopy = theDumper.myDumpBuffer.str();
-  theDumper.clear(true);
+  theDumper.clearNotDumped();
+  theDumper.myDumpStorage->reserveBuffer();
   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
   for (; anIt != aNotDumped.end(); ++anIt) {
     // if the feature is composite, dump it with all subs
@@ -1247,15 +1670,42 @@ ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
     }
   }
 
-  // avoid multiple empty lines
-  size_t anInd = std::string::npos;
-  while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
-    aBufCopy.erase(anInd, 1);
-  // then store currently dumped string
-  theDumper.myFullDump << aBufCopy;
+  // then store the reserved data
+  theDumper.myDumpStorage->restoreReservedBuffer();
+  theDumper.myDumpStorage->mergeBuffer();
 
   // now, store all postponed features
   theDumper.dumpPostponed();
 
   return theDumper;
 }
+
+
+void ModelHighAPI_Dumper::exportVariables() const
+{
+  DocumentPtr aRoot = ModelAPI_Session::get()->moduleDocument();
+  EntityNameMap::const_iterator aNameIter = myNames.cbegin();
+  for(; aNameIter != myNames.end(); aNameIter++) {
+    FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNameIter->first);
+    if (aFeature.get() && aFeature->document() != aRoot) {
+      FeaturePtr aPartFeat = ModelAPI_Tools::findPartFeature(aRoot, aFeature->document());
+      if (aPartFeat.get()) {
+        int aFeatureId = aFeature->data()->featureId();
+        int aPartId = aPartFeat->data()->featureId();
+        std::ostringstream anEntryStr;
+        anEntryStr<<aPartId<<":"<<aFeatureId;
+        std::string anEntry = anEntryStr.str();
+        exportVariable(anEntry, aNameIter->second.myCurrentName);
+        size_t aSize = aFeature->results().size();
+        if (aSize > 1) { // additional entries for features with more than one result
+          for(size_t a = 1; a < aSize; a++) {
+            std::ostringstream aResEntryStr;
+            aResEntryStr<<anEntry<<":"<<a;
+            std::string aResEntry = aResEntryStr.str();
+            exportVariable(aResEntry, aNameIter->second.myCurrentName);
+          }
+        }
+      }
+    }
+  }
+}