Salome HOME
Merge C++ part of kleontev/38044_auto_repair
[modules/geom.git] / src / GEOM / GEOM_Engine.cxx
index b586ff24b5175ea2da622515ff95ebb432facdd5..13aea9601b606aade95657920fd67df2db75b5ff 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -37,6 +37,7 @@
 #include "utilities.h"
 
 #include <Basics_Utils.hxx>
+#include <Basics_OCCTVersion.hxx>
 
 #include <TDF_Tool.hxx>
 #include <TDF_Data.hxx>
@@ -62,7 +63,9 @@
 
 #include <TColStd_DataMapIteratorOfDataMapOfIntegerTransient.hxx>
 
+#if OCC_VERSION_LARGE < 0x07050000
 #include <Resource_DataMapIteratorOfDataMapOfAsciiStringAsciiString.hxx>
+#endif
 
 #include <BinDrivers.hxx>
 #include <StdDrivers_DocumentRetrievalDriver.hxx>
 #define C_SQR_BRACKET ']'
 #define PY_NULL "None"
 
-#ifdef _DEBUG_
-static int MYDEBUG = 0;
-#else
-static int MYDEBUG = 0;
-#endif
-
 // VSR 29/08/2017: 0023327, 0023428: eliminate unnecessary lines in Python dump
 // Next macro, when defined, causes appearing of SubShapeAllIDs(), SubShapeAllSortedIDs(), GetSameIDs()
 // and other such commands in Python dump.
@@ -113,7 +110,7 @@ bool ProcessFunction(Handle(GEOM_Function)&             theFunction,
                      TCollection_AsciiString&           theScript,
                      TCollection_AsciiString&           theAfterScript,
                      const TVariablesList&              theVariables,
-                     const bool                         theIsPublished,
+                     const bool                         /*theIsPublished*/,
                      TDF_LabelMap&                      theProcessed,
                      std::set<TCollection_AsciiString>& theIgnoreObjs,
                      bool&                              theIsDumpCollected);
@@ -155,6 +152,124 @@ static TCollection_AsciiString GetPublishCommands
 
 void Prettify(TCollection_AsciiString& theScript);
 
+// Helper functions
+namespace
+{
+  // Specifies a way to process a given function
+  enum FunctionProcessType { NOT_PROCESS, TO_PROCESS, UPDATE_DESCRIPTION };
+
+  // Starting string for an edge case where function was dumped from Python code
+  const Standard_CString funcFromPythonStartString = "from salome.geom.geomrepairadv";
+
+
+  //================================================================================
+  /*!
+  * \brief Checks if a description contents a function that was dumped from Python code
+  */
+  //================================================================================
+  bool IsFunctionSetFromPython(const TCollection_AsciiString& aDescr)
+  {
+    // TODO: make it more generic and not depended on geomrepairadv name
+    return aDescr.Search(funcFromPythonStartString) != -1;
+  }
+
+
+  //================================================================================
+  /*!
+  * \brief Removes from description the part before specific import statement
+  */
+  //================================================================================
+  void UpdateFuncFromPythonDescription(TCollection_AsciiString& aDescr)
+  {
+    const Standard_Integer startStringPos = aDescr.Search(funcFromPythonStartString);
+    MESSAGE("Description should start from pos: " << startStringPos);
+    if (startStringPos == -1)
+    {
+      MESSAGE("Can't find a string:\n" << funcFromPythonStartString << " \nin func description!");
+      return;
+    }
+
+    // Remove everything from the beginning till the starting point.
+    // Index starts from 1 not 0!
+    aDescr.Remove(1, startStringPos - 1);
+    MESSAGE("Updated func description: " << aDescr);
+  }
+
+
+  //================================================================================
+  /*!
+  * \brief Finds out how we should process a given function for Python dump
+  */
+  //================================================================================
+  FunctionProcessType GetFunctionProcessingType(const Handle(GEOM_Function)& theFunction, const TDF_LabelMap& theProcessed, const TCollection_AsciiString& aDescr)
+  {
+    MESSAGE("Start check function dependencies...");
+
+    TDF_LabelSequence aSeq;
+    theFunction->GetDependency(aSeq);
+    const Standard_Integer aLen = aSeq.Length();
+
+    for (Standard_Integer i = 1; i <= aLen; i++) {
+      TDF_Label aRefLabel = aSeq.Value(i);
+      Handle(TDF_Reference) aRef;
+      if (!aRefLabel.FindAttribute(TDF_Reference::GetID(), aRef)) {
+        MESSAGE("Can't find TDF_Reference::GetID() attribute. Do not process.");
+        return NOT_PROCESS;
+      }
+
+      if (aRef.IsNull() || aRef->Get().IsNull()) {
+        MESSAGE("Reference to attribute is null. Do not process.");
+        return NOT_PROCESS;
+      }
+
+      Handle(TDataStd_TreeNode) aT;
+      if (!TDataStd_TreeNode::Find(aRef->Get(), aT)) {
+        MESSAGE("Can't find a tree node. Do not process.");
+        return NOT_PROCESS;
+      }
+
+      TDF_Label aDepLabel = aT->Label();
+      Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(aDepLabel);
+      if (aFunction.IsNull()) {
+        MESSAGE("Function is null. Do not process." << aFunction->GetDescription());
+        return NOT_PROCESS;
+      }
+
+      if (!theProcessed.Contains(aDepLabel)) {
+        // Special case for function dumped from Python, because it's appended to
+        // description of other function that should be rejected early.
+        // TODO: it's not clear if we need to check every given function or
+        // checking on this level is enough. At this moment it's better to stay here 
+        // for performance reason.
+        if (IsFunctionSetFromPython(aDescr)) {
+          MESSAGE("Function set from Python. Do process with updated description.");
+          return UPDATE_DESCRIPTION;
+        }
+
+        MESSAGE("The dependency label is not in processed list. Do not process.");
+        return NOT_PROCESS;
+      }
+    }
+
+    MESSAGE("OK. Do process the function.");
+    return TO_PROCESS;
+  }
+
+  //================================================================================
+  /*!
+  * \brief Adds function's object to ignored for Python dump output
+  */
+  //================================================================================
+  void AddFuncObjectToIgnored(const Handle(GEOM_Function)& theFunction, std::set<TCollection_AsciiString>& theIgnoreObjs)
+  {
+    TCollection_AsciiString anObjEntry;
+    TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
+    theIgnoreObjs.insert(anObjEntry);
+  }
+
+}
+
+
 //================================================================================
 /*!
  * \brief Fix up the name of python variable
@@ -512,9 +627,13 @@ bool GEOM_Engine::Save(const char* theFileName)
 {
   if(!_document) return false;
 
-  _OCAFApp->SaveAs(_document, theFileName);
+#if defined(WIN32) && defined(UNICODE)
+  std::wstring aFileName = Kernel_Utils::utf8_decode_s(theFileName);
+#else
+  std::string aFileName = theFileName;
+#endif
 
-  return true;
+  return _OCAFApp->SaveAs( _document, aFileName.c_str() ) == PCDM_SS_OK;
 }
 
 //=============================================================================
@@ -524,8 +643,13 @@ bool GEOM_Engine::Save(const char* theFileName)
 //=============================================================================
 bool GEOM_Engine::Load(const char* theFileName)
 {
+#if defined(WIN32) && defined(UNICODE)
+       std::wstring aFileName = Kernel_Utils::utf8_decode_s(theFileName);
+#else
+       std::string aFileName = theFileName;
+#endif
   Handle(TDocStd_Document) aDoc;
-  if (_OCAFApp->Open(theFileName, aDoc) != PCDM_RS_OK) {
+  if (_OCAFApp->Open(aFileName.c_str(), aDoc) != PCDM_RS_OK) {
     return false;
   }
 
@@ -1078,62 +1202,62 @@ bool ProcessFunction(Handle(GEOM_Function)&             theFunction,
                      bool&                              theIsDumpCollected)
 {
   theIsDumpCollected = false;
-  if (theFunction.IsNull()) return false;
 
-  if (theProcessed.Contains(theFunction->GetEntry())) return false;
+  if (theFunction.IsNull()) {
+    MESSAGE("Can't process a null function! Return.");
+    return false;
+  }
 
-  // pass functions, that depends on nonexisting ones
-  bool doNotProcess = false;
-  TDF_LabelSequence aSeq;
-  theFunction->GetDependency(aSeq);
-  Standard_Integer aLen = aSeq.Length();
-  for (Standard_Integer i = 1; i <= aLen && !doNotProcess; i++) {
-    TDF_Label aRefLabel = aSeq.Value(i);
-    Handle(TDF_Reference) aRef;
-    if (!aRefLabel.FindAttribute(TDF_Reference::GetID(), aRef)) {
-      doNotProcess = true;
-    }
-    else {
-      if (aRef.IsNull() || aRef->Get().IsNull()) {
-        doNotProcess = true;
-      }
-      else {
-        Handle(TDataStd_TreeNode) aT;
-        if (!TDataStd_TreeNode::Find(aRef->Get(), aT)) {
-          doNotProcess = true;
-        }
-        else {
-          TDF_Label aDepLabel = aT->Label();
-          Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(aDepLabel);
+  TCollection_AsciiString aDescr = theFunction->GetDescription();
+  MESSAGE("The function description: " << aDescr);
 
-          if (aFunction.IsNull()) doNotProcess = true;
-          else if (!theProcessed.Contains(aDepLabel)) doNotProcess = true;
-        }
-      }
-    }
-  }
+  if (theProcessed.Contains(theFunction->GetEntry()))
+    return false;
 
-  if (doNotProcess) {
-    TCollection_AsciiString anObjEntry;
-    TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
-    theIgnoreObjs.insert(anObjEntry);
+  // Check if a given function depends on nonexisting ones
+  const FunctionProcessType funcProcessType = GetFunctionProcessingType(theFunction, theProcessed, aDescr);
+  switch (funcProcessType)
+  {
+  case TO_PROCESS:
+    // Just process it
+    break;
+
+  case NOT_PROCESS:
+  {
+    // We don't need this function and its object in a dump
+    AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
     return false;
   }
+
+  case UPDATE_DESCRIPTION:
+    // Edge case for a function that was dumped from Python.
+    // Get rid of the parent function description.
+    UpdateFuncFromPythonDescription(aDescr);
+    // A result object is already added by an algo script, then
+    // if we keep it in the dump it will be added twice on the script loading.
+    AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
+    break;
+
+  default:
+    MESSAGE("Wrong type of the function processing!" << funcProcessType);
+    break;
+  }
+
   theProcessed.Add(theFunction->GetEntry());
 
-  TCollection_AsciiString aDescr = theFunction->GetDescription();
-  if(aDescr.Length() == 0) return false;
+  // Check the length only after its fucntion was added to the processed!
+  if(!aDescr.Length())
+    return false;
 
-  //Check if its internal function which doesn't requires dumping
-  if(aDescr == "None") return false;
+  // Check if it's an internal function which doesn't require dumping
+  if(aDescr == "None")
+    return false;
 
   //Check the very specific case of RestoreShape function,
   //which is not dumped, but the result can be published by the user.
   //We do not publish such objects to decrease danger of dumped script failure.
   if(aDescr.Value(1) == '#') {
-    TCollection_AsciiString anObjEntry;
-    TDF_Tool::Entry(theFunction->GetOwnerEntry(), anObjEntry);
-    theIgnoreObjs.insert(anObjEntry);
+    AddFuncObjectToIgnored(theFunction, theIgnoreObjs);
     return false;
   }
 
@@ -1150,6 +1274,9 @@ bool ProcessFunction(Handle(GEOM_Function)&             theFunction,
 
   //Replace parameter by notebook variables
   ReplaceVariables(aDescr,theVariables);
+  // Check description, because we could lose entire command during variable processing
+  if (aDescr.IsEmpty())
+    return false;
 
   //Process sketcher functions, replacing string command by calls to Sketcher interface
   if ( ( aDescr.Search( "MakeSketcherOnPlane" ) != -1 ) || ( aDescr.Search( "MakeSketcher" ) != -1 ) ) {
@@ -1289,14 +1416,13 @@ Handle(TColStd_HSequenceOfInteger) FindEntries(TCollection_AsciiString& theStrin
 void ReplaceVariables(TCollection_AsciiString& theCommand,
                       const TVariablesList&    theVariables)
 {
-  if (MYDEBUG)
-    cout<<"Command : "<<theCommand<<endl;
+  MESSAGE("Command : " << theCommand);
 
-  if (MYDEBUG) {
-    cout<<"All Entries:"<<endl;
+  if (SALOME::VerbosityActivated()) {
+    std::cout<<"All Entries:"<<std::endl;
     TVariablesList::const_iterator it = theVariables.begin();
     for(;it != theVariables.end();it++)
-      cout<<"\t'"<<(*it).first<<"'"<<endl;
+      std::cout<<"\t'"<<(*it).first<<"'"<<std::endl;
   }
 
   //Additional case - multi-row commands
@@ -1306,8 +1432,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
     if( aCommand.Length() == 0 )
       break;
 
-    if (MYDEBUG)
-      cout<<"Sub-command : "<<aCommand<<endl;
+    MESSAGE("Sub-command : " << aCommand);
 
     Standard_Integer aStartCommandPos = theCommand.Location(aCommand,1,theCommand.Length());
     Standard_Integer aEndCommandPos = aStartCommandPos + aCommand.Length();
@@ -1324,8 +1449,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
     //Remove white spaces
     anEntry.RightAdjust();
     anEntry.LeftAdjust();
-    if(MYDEBUG)
-      cout<<"Result entry : '" <<anEntry<<"'"<<endl;
+    MESSAGE("Result entry : '" << anEntry << "'");
 
     if ( anEntry.IsEmpty() ) {
       aCommandIndex++;
@@ -1342,8 +1466,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
       anEntry.Remove( 1, 1 );
       anEntry.RightAdjust();
       anEntry.LeftAdjust();
-      if(MYDEBUG)
-        cout<<"Sub-entry : '" <<anEntry<<"'"<<endl;
+      MESSAGE("Sub-entry : '" << anEntry << "'");
     }
 
     //Find variables used for object construction
@@ -1353,18 +1476,22 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
       aStates = (*it).second;
 
     if(!aStates) {
-      if(MYDEBUG)
-        cout<<"Valiables list empty!!!"<<endl;
+      MESSAGE("Can't find an entry among study objects!");
+      // We can't skip this because the entry can be used with automatically assigned name
+      // like "geomObj_1" to create other objects. Some tests will fail without this.
+      // MESSAGE("Can't find an entry among study objects! Skip this command.");
+      // theCommand.Remove(aStartCommandPos, aEndCommandPos - aStartCommandPos);
       aCommandIndex++;
+
       continue;
     }
 
     TState aVariables = aStates->GetCurrectState();
 
-    if(MYDEBUG) {
-      cout<<"Variables from SObject:"<<endl;
+    if(SALOME::VerbosityActivated()) {
+          std::cout<<"Variables from SObject:"<<std::endl;
       for (size_t i = 0; i < aVariables.size();i++)
-        cout<<"\t Variable["<<i<<"] = "<<aVariables[i].myVariable<<endl;
+        std::cout<<"\t Variable["<<i<<"] = "<<aVariables[i].myVariable<<std::endl;
     }
 
     //Calculate total number of parameters
@@ -1372,8 +1499,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
     while(aCommand.Location(aTotalNbParams,COMMA,1,aCommand.Length()))
       aTotalNbParams++;
 
-    if(MYDEBUG)
-      cout<<"aTotalNbParams = "<<aTotalNbParams<<endl;
+    MESSAGE("aTotalNbParams = " << aTotalNbParams);
 
     Standard_Integer aFirstParam = aNbEntries;
 
@@ -1404,6 +1530,8 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
         aStartPos = aCommand.Location(i-1, COMMA, 1, aCommand.Length()) + 2;
         aEndPos = aCommand.Location(i, COMMA, 1, aCommand.Length());
       }
+      if (aStartPos == 0 || aEndPos == 0)
+        continue;
 
       if( aCommand.Value( aStartPos ) == O_SQR_BRACKET )
         aStartPos++;
@@ -1412,15 +1540,13 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
       if ( aStartPos == aEndPos )
         continue; // PAL20889: for "[]"
 
-      if(MYDEBUG)
-        cout<<"aStartPos = "<<aStartPos<<", aEndPos = "<<aEndPos<<endl;
+      MESSAGE("aStartPos = " << aStartPos << ", aEndPos = " << aEndPos);
 
       aVar = aCommand.SubString(aStartPos, aEndPos-1);
       aVar.RightAdjust();
       aVar.LeftAdjust();
 
-      if(MYDEBUG)
-        cout<<"Variable: '"<< aVar <<"'"<<endl;
+      MESSAGE("Variable: '" << aVar << "'");
 
       // specific case for sketcher
       if(aVar.Location( TCollection_AsciiString("Sketcher:"), 1, aVar.Length() ) != 0) {
@@ -1439,8 +1565,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
             aEndSectionPos = aVar.Length();
 
           aSection = aVar.SubString(aStartSectionPos, aEndSectionPos-1);
-          if(MYDEBUG)
-            cout<<"aSection: "<<aSection<<endl;
+          MESSAGE("aSection: " << aSection);
 
           Standard_Integer aNbParams = 1;
           while( aSection.Location( aNbParams, ' ', 1, aSection.Length() ) )
@@ -1456,15 +1581,13 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
             else
               aEndParamPos = aSection.Length() + 1;
 
-            if(MYDEBUG)
-              cout<<"aParamIndex: "<<aParamIndex<<" aStartParamPos: " <<aStartParamPos<<" aEndParamPos: "<<aEndParamPos<<endl;
+            MESSAGE("aParamIndex: " << aParamIndex << " aStartParamPos: " << aStartParamPos << " aEndParamPos: " << aEndParamPos);
 
             if ( aStartParamPos == aEndParamPos)
               continue;
 
             aParameter = aSection.SubString(aStartParamPos, aEndParamPos-1);
-            if(MYDEBUG)
-              cout<<"aParameter: "<<aParameter<<endl;
+            MESSAGE("aParameter: " << aParameter);
 
             if(iVar >= aVariables.size())
               continue;
@@ -1480,28 +1603,25 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
               aReplacedParameter.InsertAfter(aReplacedParameter.Length(),"'");
             }
 
-            if(MYDEBUG)
-              cout<<"aSection before : "<<aSection<<endl;
+            MESSAGE("aSection before : " << aSection);
             aSection.Remove(aStartParamPos, aEndParamPos - aStartParamPos);
             aSection.Insert(aStartParamPos, aReplacedParameter);
-            if(MYDEBUG)
-              cout<<"aSection after  : "<<aSection<<endl<<endl;
+            MESSAGE("aSection after  : " << aSection << '\n');
             iVar++;
           }
-          if(MYDEBUG)
-            cout<<"aVar before : "<<aVar<<endl;
+
+          MESSAGE("aVar before : " << aVar);
+
           aVar.Remove(aStartSectionPos, aEndSectionPos - aStartSectionPos);
           aVar.Insert(aStartSectionPos, aSection);
-          if(MYDEBUG)
-            cout<<"aVar after  : "<<aVar<<endl<<endl;
+
+          MESSAGE("aVar after  : " << aVar << '\n');
         }
 
-        if(MYDEBUG)
-          cout<<"aCommand before : "<<aCommand<<endl;
+        MESSAGE("aCommand before : " << aCommand);
         aCommand.Remove(aStartPos, aEndPos - aStartPos);
         aCommand.Insert(aStartPos, aVar);
-        if(MYDEBUG)
-          cout<<"aCommand after  : "<<aCommand<<endl;
+        MESSAGE("aCommand after  : " << aCommand);
 
         break;
       } // end of specific case for sketcher
@@ -1537,8 +1657,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
     aStates->IncrementState();
   }
 
-  if (MYDEBUG)
-    cout<<"Command : "<<theCommand<<endl;
+  MESSAGE("Command after replacing of the variables: " << theCommand);
 }
 
 //=============================================================================
@@ -1548,7 +1667,7 @@ void ReplaceVariables(TCollection_AsciiString& theCommand,
 //=============================================================================
 void ReplaceEntriesByNames (TCollection_AsciiString&                  theScript,
                             TSting2ObjDataMap&                        aEntry2ObjData,
-                            const bool                                theIsPublished,
+                            const bool                                /*theIsPublished*/,
                             TColStd_SequenceOfAsciiString&            theObjListToPublish,
                             Standard_Integer&                         objectCounter,
                             Resource_DataMapOfAsciiStringAsciiString& aNameToEntry)
@@ -1821,6 +1940,8 @@ TCollection_AsciiString GetPublishCommands
 
   if (!thePublished.count(theTag)) {
     // This object is not published yet.
+    thePublished.insert(theTag);
+
     std::map< int, TCollection_AsciiString >::const_iterator anIt =
       theEntryToCmdMap.find(theTag);
 
@@ -1829,7 +1950,7 @@ TCollection_AsciiString GetPublishCommands
       TIntToListIntMap::const_iterator aRefIt = theMapRefs.find(theTag);
 
       if (aRefIt != theMapRefs.end()) {
-        // Recursively publish all references.
+        // Recursively publish all references.         
         std::list< int >::const_iterator aRefTagIt = aRefIt->second.begin();
 
         for(; aRefTagIt != aRefIt->second.end(); ++aRefTagIt) {
@@ -1843,8 +1964,6 @@ TCollection_AsciiString GetPublishCommands
       // Add the object command.
       aResult += anIt->second;
     }
-
-    thePublished.insert(theTag);
   }
 
   return aResult;