Salome HOME
Fix unit tests for ParametersPlugin
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.cpp
index bbf3be1d0b188845e951282ebeeee317539c8107..28961598fd5ecfc827f2fb82c77e7624d4616aca 100644 (file)
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeRefAttr.h>
+#include <ModelAPI_AttributeRefAttrList.h>
+#include <ModelAPI_AttributeReference.h>
+#include <ModelAPI_AttributeRefList.h>
 #include <ModelAPI_AttributeSelection.h>
+#include <ModelAPI_AttributeSelectionList.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_CompositeFeature.h>
 #include <ModelAPI_Document.h>
 
 #include <OSD_OpenFile.hxx>
 
-#include <algorithm>
 #include <fstream>
 
-//#define DUMP_USER_DEFINED_NAMES
+#define DUMP_USER_DEFINED_NAMES
+
+static int gCompositeStackDepth = 0;
 
 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
 
@@ -54,13 +59,35 @@ ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
   return mySelf;
 }
 
-void ModelHighAPI_Dumper::clear()
+#define CLEAR_STREAM(theStream) { \
+    std::ostringstream anOther;   \
+    swap(theStream, anOther);     \
+  }
+
+void ModelHighAPI_Dumper::clear(bool bufferOnly)
 {
-  myDumpBuffer = std::ostringstream();
+  CLEAR_STREAM(myDumpBuffer);
   myDumpBuffer << std::setprecision(16);
+
+  clearNotDumped();
+
+  if (!bufferOnly) {
+    CLEAR_STREAM(myFullDump);
+    myFullDump << std::setprecision(16);
+
+    myNames.clear();
+    myModules.clear();
+    myFeatureCount.clear();
+    myLastEntityWithName = EntityPtr();
+  }
 }
 
-const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity)
+void ModelHighAPI_Dumper::clearNotDumped()
+{
+  myNotDumpedEntities.clear();
+}
+
+const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity, bool theSaveNotDumped)
 {
   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
   if (aFound != myNames.end())
@@ -68,23 +95,34 @@ const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity)
 
   // entity is not found, store it
   std::string aName;
-  bool isNameDefined = false;
+  bool isUserDefined = false;
   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
   if (aFeature) {
+    isUserDefined = true;
     aName = aFeature->name();
-    isNameDefined = !aName.empty();
-
-    if (!isNameDefined) {
-      static long anIndex = 0;
-      // set default name: feature ID + index
-      std::ostringstream aConverter;
-      aConverter << aFeature->getKind() << "_" << ++anIndex;
-      aName = aConverter.str();
-      std::transform(aName.begin(), aName.end(), aName.begin(), ::tolower);
+    const std::string& aKind = aFeature->getKind();
+    DocumentPtr aDoc = aFeature->document();
+    int& aNbFeatures = myFeatureCount[aDoc][aKind];
+
+    size_t anIndex = aName.find(aKind);
+    if (anIndex == 0 && aName[aKind.length()] == '_') { // name starts with "FeatureKind_"
+      std::string anIdStr = aName.substr(aKind.length() + 1, std::string::npos);
+      int anId = std::stoi(anIdStr);
+
+      // Check number of already registered objects of such kind. Index of current object
+      // should be the same to identify feature's name as automatically generated.
+      if (aNbFeatures + 1 == anId) {
+        isUserDefined = false;
+        //aNbFeatures = anId - 1;
+      }
     }
+
+    aNbFeatures += 1;
   }
 
-  myNames[theEntity] = std::pair<std::string, bool>(aName, isNameDefined);
+  myNames[theEntity] = std::pair<std::string, bool>(aName, isUserDefined);
+  if (theSaveNotDumped)
+    myNotDumpedEntities.insert(theEntity);
   return myNames[theEntity].first;
 }
 
@@ -118,44 +156,87 @@ bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theD
 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
 {
   bool isOk = true;
-  // dump all features
   std::list<FeaturePtr> aFeatures = theDoc->allFeatures();
   std::list<FeaturePtr>::const_iterator aFeatIt = aFeatures.begin();
-  for (; aFeatIt != aFeatures.end(); ++aFeatIt) {
-    dumpFeature(*aFeatIt);
-    // iteratively process composite features
+  // firstly, dump all parameters
+  for (; aFeatIt != aFeatures.end(); ++ aFeatIt)
+    dumpParameter(*aFeatIt);
+  // dump all other features
+  for (aFeatIt = aFeatures.begin(); aFeatIt != aFeatures.end(); ++aFeatIt) {
     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFeatIt);
-    if (!aCompFeat)
-      continue;
-
-    // sub-part is processed independently, because it provides separate document
-    if ((*aFeatIt)->getKind() == PartSetPlugin_Part::ID()) {
-      ResultPartPtr aPartResult =
-          std::dynamic_pointer_cast<ModelAPI_ResultPart>((*aFeatIt)->lastResult());
-      if (!aPartResult)
-        continue;
-      DocumentPtr aSubDoc = aPartResult->partDoc();
-      // set name of document equal to part name
-      myNames[aSubDoc] = myNames[*aFeatIt];
-
-      isOk = process(aSubDoc) && isOk;
-    } else 
+    if (aCompFeat) // iteratively process composite features
       isOk = process(aCompFeat) && isOk;
+    else if (!isDumped(*aFeatIt)) // dump common feature 
+      dumpFeature(*aFeatIt);
   }
   return isOk;
 }
 
-bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite)
+bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite, bool isForce)
 {
+  // increase composite features stack
+  ++gCompositeStackDepth;
+  // dump composite itself
+  if (!isDumped(theComposite) || isForce)
+    dumpFeature(theComposite, isForce);
+
+  // sub-part is processed independently, because it provides separate document
+  if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
+    // decrease composite features stack because we run into separate document
+    --gCompositeStackDepth;
+
+    ResultPartPtr aPartResult =
+        std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
+    if (!aPartResult)
+      return false;
+    DocumentPtr aSubDoc = aPartResult->partDoc();
+    // set name of document
+    const std::string& aPartName = myNames[theComposite].first;
+    std::string aDocName = aPartName + "_doc";
+    myNames[aSubDoc] = std::pair<std::string, bool>(aDocName, false);
+
+    // dump document in a separate line
+    *this << aDocName << " = " << aPartName << ".document()" << std::endl;
+    // dump features in the document
+    return process(aSubDoc);
+  }
+
+  // dump sub-features
+  bool isOk = processSubs(theComposite);
+  // decrease composite features stack
+  --gCompositeStackDepth;
+
+  return isOk;
+}
+
+bool ModelHighAPI_Dumper::processSubs(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
+                                      bool theDumpModelDo)
+{
+  bool isOk = true;
   // dump all sub-features;
   int aNbSubs = theComposite->numberOfSubs();
+//////////////////////////////////////
+  std::list<ObjectPtr> aList = theComposite->reflist("Features")->list();
+//////////////////////////////////////
   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
     FeaturePtr aFeature = theComposite->subFeature(anIndex);
-    dumpFeature(aFeature, true);
+    if (isDumped(aFeature))
+      continue;
+
+    CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
+    if (aCompFeat) // iteratively process composite features
+      isOk = process(aCompFeat) && isOk;
+    else
+      dumpFeature(aFeature, true);
   }
-  // dump command to update model
-  myDumpBuffer << "model.do()" << std::endl;
-  return true;
+
+  // It is necessary for the sketch to create its result when complete (command "model.do()").
+  // This option is set by flat theDumpModelDo.
+  // However, nested sketches are rebuilt by parent feature, so, they do not need
+  // explicit call of "model.do()". This will be controlled by the depth of the stack.
+  if (theDumpModelDo && gCompositeStackDepth <= 1)
+    *this << "model.do()" << std::endl;
+  return isOk;
 }
 
 bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
@@ -188,7 +269,7 @@ bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
   aFile << "model.begin()" << std::endl;
 
   // dump collected data
-  aFile << myDumpBuffer.str();
+  aFile << myFullDump.str();
 
   // standard footer
   aFile << "model.end()" << std::endl;
@@ -218,9 +299,16 @@ void ModelHighAPI_Dumper::dumpEntitySetName()
     myDumpBuffer << ".feature()";
   myDumpBuffer << ".data().setName(\"" << aName << "\")" << std::endl;
 #endif
+  myNames[myLastEntityWithName].second = false; // don't dump "setName" for the entity twice
   myLastEntityWithName = EntityPtr();
 }
 
+bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
+{
+  EntityNameMap::const_iterator aFound = myNames.find(theEntity);
+  return aFound != myNames.end();
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
 {
   myDumpBuffer << theChar;
@@ -239,6 +327,12 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theStrin
   return *this;
 }
 
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
+{
+  myDumpBuffer << (theValue ? "True" : "False");
+  return *this;
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
 {
   myDumpBuffer << theValue;
@@ -343,11 +437,50 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
   return *this;
 }
 
-ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const EntityPtr& theEntity)
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
 {
   myDumpBuffer << name(theEntity);
   if (myNames[theEntity].second)
     myLastEntityWithName = theEntity;
+  myNotDumpedEntities.erase(theEntity);
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
+{
+  FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
+  int anIndex = 0;
+  std::list<ResultPtr> aResults = aFeature->results();
+  for(std::list<ResultPtr>::const_iterator anIt = aResults.cbegin(); anIt != aResults.cend(); ++anIt, ++anIndex) {
+    if(theResult->isSame(*anIt)) {
+      break;
+    }
+  }
+  myDumpBuffer << name(aFeature) << ".result()[" << anIndex << "]";
+  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);
+    return *this;
+  }
+
+  ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
+  if(aResult.get()) {
+    *this << aResult;
+    return *this;
+  }
+
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
+{
+  FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
+  myDumpBuffer << name(anOwner) << "." << attributeGetter(anOwner, theAttr->id()) << "()";
   return *this;
 }
 
@@ -355,11 +488,74 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
 {
   if (theRefAttr->isObject())
-    myDumpBuffer << name(theRefAttr->object());
-  else {
-    AttributePtr anAttr = theRefAttr->attr();
-    FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner());
-    myDumpBuffer << name(anOwner) << "." << attributeGetter(anOwner, anAttr->id()) << "()";
+    *this << theRefAttr->object();
+  else
+    *this << theRefAttr->attr();
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
+{
+  myDumpBuffer << "[";
+  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 << ", ";
+    else
+      isAdded = true;
+    if (anIt->first)
+      *this << anIt->first;
+    else if (anIt->second)
+      * this << anIt->second;
+  }
+  myDumpBuffer << "]";
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
+{
+  *this << theReference->value();
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
+{
+  static const int aThreshold = 2;
+  // 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 << "[";
+    std::list<ObjectPtr> aList = theRefList->list();
+    bool isAdded = false;
+    std::list<ObjectPtr>::const_iterator anIt = aList.begin();
+    for (; anIt != aList.end(); ++anIt) {
+      if (isAdded)
+        myDumpBuffer << ", ";
+      else
+        isAdded = true;
+
+      *this << *anIt;
+    }
+    myDumpBuffer << "]";
+  } else {
+    // clear buffer and store list "as is"
+    CLEAR_STREAM(myDumpBuffer);
+    *this << theRefList;
+    // save buffer and clear it again
+    std::string aDumpedList = myDumpBuffer.str();
+    CLEAR_STREAM(myDumpBuffer);
+    // obtain 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;
   }
   return *this;
 }
@@ -367,7 +563,57 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
 {
-  myDumpBuffer << "\"" << theAttrSelect->namingName() << "\"";
+  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;
+  }
+
+  myDumpBuffer << "\"" << aShape->shapeTypeStr() << "\", \"" << theAttrSelect->namingName() << "\")";
+  return *this;
+}
+
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
+{
+  myDumpBuffer << "[";
+
+  GeomShapePtr aShape;
+  std::string aShapeTypeStr;
+
+  bool isAdded = false;
+
+  for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
+    AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
+    aShape = anAttribute->value();
+    if(!aShape.get()) {
+      aShape = anAttribute->context()->shape();
+    }
+
+    if(!aShape.get()) {
+      continue;
+    }
+
+    if(isAdded) {
+      myDumpBuffer << ", ";
+    } else {
+      isAdded = true;
+    }
+    myDumpBuffer << "model.selection(\"" << aShape->shapeTypeStr() << "\", \"" << anAttribute->namingName() << "\")";
+  }
+
+  myDumpBuffer << "]";
   return *this;
 }
 
@@ -378,5 +624,30 @@ ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
 {
   theDumper.myDumpBuffer << theEndl;
   theDumper.dumpEntitySetName();
+
+  // store all not-dumped entities first
+  std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
+  std::string aBufCopy = theDumper.myDumpBuffer.str();
+  theDumper.clear(true);
+  std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
+  for (; anIt != aNotDumped.end(); ++anIt) {
+    // if the feature is composite, dump it with all subs
+    CompositeFeaturePtr aCompFeat =
+        std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
+    if (aCompFeat)
+      theDumper.process(aCompFeat, true);
+    else {
+      FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
+      theDumper.dumpFeature(aFeature, true);
+    }
+  }
+
+  // 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;
+
   return theDumper;
 }