Salome HOME
Regression of SALOME_TESTS/Grids/smesh/imps_08/I8
[modules/smesh.git] / src / SMESH_I / SMESH_2smeshpy.cxx
index 60c0b2491a8bff050377059d5e62baf3a723e1e7..929da0dcf43bc2518b5e946ff3dd1d90e88374f8 100644 (file)
@@ -44,7 +44,7 @@
 
 #include <LDOMParser.hxx>
 
-#ifdef WNT
+#ifdef WIN32
 #include <windows.h>
 #else
 #include <unistd.h>
@@ -183,7 +183,8 @@ namespace {
     if ( cmd->GetString().Location( TPythonDump::NotPublishedObjectName(), 1, cmd->Length() ))
     {
       bool isResultPublished = false;
-      for ( int i = 0; i < cmd->GetNbResultValues(); i++ )
+      const int nbRes = cmd->GetNbResultValues();
+      for ( int i = 0; i < nbRes; i++ )
       {
         _pyID objID = cmd->GetResultValue( i+1 );
         if ( cmd->IsStudyEntry( objID ))
@@ -198,8 +199,17 @@ namespace {
     }
     // check if an Object was created in the script
     _AString comment;
-    const _pyID& obj = cmd->GetObject();
-    if ( !obj.IsEmpty() && cmd->IsStudyEntry( obj ) && !presentObjects.count( obj ))
+
+    _pyID obj = cmd->GetObject();
+    if ( obj.Search( "print " ) == 1 )
+      return; // print statement
+
+    if ( !obj.IsEmpty() && obj.Value( obj.Length() ) == ')' )
+      // remove an accessor method
+      obj = _pyCommand( obj ).GetObject();
+
+    const bool isMethodCall = cmd->IsMethodCall();
+    if ( !obj.IsEmpty() && isMethodCall && !presentObjects.count( obj ) )
     {
       comment = "not created Object";
       theGen->ObjectCreationRemoved( obj );
@@ -218,6 +228,8 @@ namespace {
           comment += *id + " has not been yet created";
           break;
         }
+      // if ( idList.empty() && cmd->IsID( arg ) && !presentObjects.count( arg ))
+      //   comment += arg + " has not been yet created";
     }
     // treat result objects
     const _pyID& result = cmd->GetResultValue();
@@ -226,10 +238,14 @@ namespace {
       list< _pyID > idList = cmd->GetStudyEntries( result );
       list< _pyID >::iterator id = idList.begin();
       for ( ; id != idList.end(); ++id )
+      {
         if ( comment.IsEmpty() )
           presentObjects.insert( *id );
         else
           theGen->ObjectCreationRemoved( *id ); // objID.SetName( name ) is not needed
+      }
+      if ( idList.empty() && cmd->IsID( result ))
+        presentObjects.insert( result );
     }
     // comment the command
     if ( !comment.IsEmpty() )
@@ -437,7 +453,7 @@ namespace {
 //================================================================================
 /*!
  * \brief Convert a python script using commands of smeshBuilder.py
- *  \param theScript - Input script
+ *  \param theScriptLines - Lines of the input script
  *  \param theEntry2AccessorMethod - returns method names to access to
  *         objects wrapped with python class
  *  \param theObjectNames - names of objects
@@ -448,48 +464,41 @@ namespace {
  */
 //================================================================================
 
-TCollection_AsciiString
-SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString&            theScript,
+void
+SMESH_2smeshpy::ConvertScript(std::list< TCollection_AsciiString >&     theScriptLines,
                               Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod,
                               Resource_DataMapOfAsciiStringAsciiString& theObjectNames,
                               std::set< TCollection_AsciiString >&      theRemovedObjIDs,
                               SALOMEDS::Study_ptr&                      theStudy,
                               const bool                                theToKeepAllCommands)
 {
-  theGen = new _pyGen( theEntry2AccessorMethod,
-                       theObjectNames,
-                       theRemovedObjIDs,
-                       theStudy,
-                       theToKeepAllCommands );
+  std::list< TCollection_AsciiString >::iterator lineIt;
+  // process notebook variables
+  {
+    SMESH_NoteBook aNoteBook;
 
-  // split theScript into separate commands
+    for ( lineIt = theScriptLines.begin(); lineIt != theScriptLines.end(); ++lineIt )
+      aNoteBook.AddCommand( *lineIt );
 
-  SMESH_NoteBook * aNoteBook = new SMESH_NoteBook();
+    theScriptLines.clear();
 
-  int from = 1, end = theScript.Length(), to;
-  while ( from < end && ( to = theScript.Location( "\n", from, end )))
-  {
-    if ( to != from )
-      // cut out and store a command
-      aNoteBook->AddCommand( theScript.SubString( from, to - 1 ));
-    from = to + 1;
+    aNoteBook.ReplaceVariables();
+
+    aNoteBook.GetResultLines( theScriptLines );
   }
 
-  aNoteBook->ReplaceVariables();
+  // convert to smeshBuilder.py API
 
-  TCollection_AsciiString aNoteScript = aNoteBook->GetResultScript();
-  delete aNoteBook;
-  aNoteBook = 0;
+  theGen = new _pyGen( theEntry2AccessorMethod,
+                       theObjectNames,
+                       theRemovedObjIDs,
+                       theStudy,
+                       theToKeepAllCommands );
 
-  // split theScript into separate commands
-  from = 1, end = aNoteScript.Length();
-  while ( from < end && ( to = aNoteScript.Location( "\n", from, end )))
-  {
-    if ( to != from )
-      // cut out and store a command
-      theGen->AddCommand( aNoteScript.SubString( from, to - 1 ));
-    from = to + 1;
-  }
+  for ( lineIt = theScriptLines.begin(); lineIt != theScriptLines.end(); ++lineIt )
+    theGen->AddCommand( *lineIt );
+
+  theScriptLines.clear();
 
   // finish conversion
   theGen->Flush();
@@ -511,8 +520,11 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString&            theScrip
   } while ( orderChanges );
 
   // concat commands back into a script
-  TCollection_AsciiString aScript, aPrevCmd;
+  TCollection_AsciiString aPrevCmd;
   set<_pyID> createdObjects;
+  createdObjects.insert( "smeshBuilder" );
+  createdObjects.insert( "smesh" );
+  createdObjects.insert( "theStudy" );
   for ( cmd = theGen->GetCommands().begin(); cmd != theGen->GetCommands().end(); ++cmd )
   {
 #ifdef DUMP_CONVERSION
@@ -520,17 +532,15 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString&            theScrip
 #endif
     if ( !(*cmd)->IsEmpty() && aPrevCmd != (*cmd)->GetString()) {
       CheckObjectPresence( *cmd, createdObjects );
-      aPrevCmd = (*cmd)->GetString();
-      aScript += "\n";
-      aScript += aPrevCmd;
+      if ( !(*cmd)->IsEmpty() ) {
+        aPrevCmd = (*cmd)->GetString();
+        theScriptLines.push_back( aPrevCmd );
+      }
     }
   }
-  aScript += "\n";
 
   theGen->Free();
   theGen.Nullify();
-
-  return aScript;
 }
 
 //================================================================================
@@ -630,15 +640,36 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
   // Method( SMESH.PointStruct(x,y,z)... -> Method( [x,y,z]...
   StructToList( aCommand );
 
+  const TCollection_AsciiString& method = aCommand->GetMethod();
+
   // not to erase _pySelfEraser's etc. used as args in some commands
-  std::list< _pyID >::const_iterator id = myKeepAgrCmdsIDs.begin();
-  for ( ; id != myKeepAgrCmdsIDs.end(); ++id )
-    if ( *id != objID && theCommand.Search( *id ) > id->Length() )
+  {
+#ifdef USE_STRING_FAMILY
+    std::list<_pyID>  objIDs;
+    if ( myKeepAgrCmdsIDs.IsInArgs( aCommand, objIDs ))
     {
-      Handle(_pyObject) obj = FindObject( *id );
-      if ( !obj.IsNull() )
-        obj->AddArgCmd( aCommand );
+      std::list<_pyID>::iterator objID = objIDs.begin();
+      for ( ; objID != objIDs.end(); ++objID )
+      {
+        Handle(_pyObject) obj = FindObject( *objID );
+        if ( !obj.IsNull() )
+        {
+          obj->AddArgCmd( aCommand );
+          //cout << objID << " found in " << theCommand << endl;
+        }
+      }
     }
+#else
+    std::list< _pyID >::const_iterator id = myKeepAgrCmdsIDs.begin();
+    for ( ; id != myKeepAgrCmdsIDs.end(); ++id )
+      if ( *id != objID && theCommand.Search( *id ) > id->Length() )
+      {
+        Handle(_pyObject) obj = FindObject( *id );
+        if ( !obj.IsNull() )
+          obj->AddArgCmd( aCommand );
+      }
+#endif
+  }
 
   // Find an object to process theCommand
 
@@ -667,8 +698,7 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
     else if ( aCommand->GetMethod() == "GetSubMesh" ) { // SubMesh creation
       _pyID subMeshID = aCommand->GetResultValue();
       Handle(_pySubMesh) subMesh = new _pySubMesh( aCommand );
-      CheckObjectIsReCreated( subMesh );
-      myObjects.insert( make_pair( subMeshID, subMesh ));
+      AddObject( subMesh );
     }
 
     // Method( mesh.GetIDSource([id1,id2]) -> Method( [id1,id2]
@@ -690,8 +720,6 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
 
     //addFilterUser( aCommand, theGen ); // protect filters from clearing
 
-    const TCollection_AsciiString& method = aCommand->GetMethod();
-
     // some commands of SMESH_MeshEditor create meshes and groups
     _pyID meshID, groups;
     if ( method.Search("MakeMesh") != -1 )
@@ -860,17 +888,19 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
       if ( Type == "SMESH.FT_ElemGeomType" )
       {
         // set SMESH.GeometryType instead of a numerical Threshold
-        const char* types[SMESH::Geom_BALL+1] = {
+        const int nbTypes = SMESH::Geom_BALL+1;
+        const char* types[nbTypes] = {
           "Geom_POINT", "Geom_EDGE", "Geom_TRIANGLE", "Geom_QUADRANGLE", "Geom_POLYGON",
           "Geom_TETRA", "Geom_PYRAMID", "Geom_HEXA", "Geom_PENTA", "Geom_HEXAGONAL_PRISM",
           "Geom_POLYHEDRA", "Geom_BALL" };
-        if ( -1 < iGeom && iGeom < SMESH::Geom_POLYHEDRA+1 )
+        if ( -1 < iGeom && iGeom < nbTypes )
           Threshold = SMESH + types[ iGeom ];
       }
       if (Type == "SMESH.FT_EntityType")
       {
         // set SMESH.EntityType instead of a numerical Threshold
-        const char* types[SMESH::Entity_Ball+1] = {
+        const int nbTypes = SMESH::Entity_Ball+1;
+        const char* types[nbTypes] = {
           "Entity_Node", "Entity_0D", "Entity_Edge", "Entity_Quad_Edge",
           "Entity_Triangle", "Entity_Quad_Triangle", "Entity_BiQuad_Triangle",
           "Entity_Quadrangle", "Entity_Quad_Quadrangle", "Entity_BiQuad_Quadrangle",
@@ -879,7 +909,7 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand
           "Entity_Hexa", "Entity_Quad_Hexa", "Entity_TriQuad_Hexa",
           "Entity_Penta", "Entity_Quad_Penta", "Entity_Hexagonal_Prism",
           "Entity_Polyhedra", "Entity_Quad_Polyhedra", "Entity_Ball" };
-        if ( -1 < iGeom && iGeom < SMESH::Entity_Quad_Polyhedra+1 )
+        if ( -1 < iGeom && iGeom < nbTypes )
           Threshold = SMESH + types[ iGeom ];
       }
     }
@@ -951,11 +981,11 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
       method == "CreateMeshesFromCGNS" ||
       method == "CreateMeshesFromGMF" ) // command result is ( [mesh1,mesh2], status )
   {
-    for ( int ind = 0; ind < theCommand->GetNbResultValues(); ind++ )
+    std::list< _pyID > meshIDs = theCommand->GetStudyEntries( theCommand->GetResultValue() );
+    std::list< _pyID >::iterator meshID = meshIDs.begin();
+    for ( ; meshID != meshIDs.end(); ++meshID )
     {
-      _pyID meshID = theCommand->GetResultValue(ind+1);
-      if ( !theCommand->IsStudyEntry( meshID ) ) continue;
-      Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue(ind+1));
+      Handle(_pyMesh) mesh = new _pyMesh( theCommand, *meshID );
       AddObject( mesh );
     }
     if ( method == "CreateMeshesFromGMF" )
@@ -1023,7 +1053,7 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand )
        method == "CreateMeasurements" )
   {
     Handle(_pyObject) obj = new _pySelfEraser( theCommand );
-    if ( !myObjects.insert( make_pair( obj->GetID(), obj )).second )
+    if ( !AddObject( obj ) )
       theCommand->Clear(); // already created
   }
   // Concatenate( [mesh1, ...], ... )
@@ -1119,10 +1149,15 @@ void _pyGen::Flush()
         id_hyp->second->GetCreationCmd()->SetObject( SMESH_2smeshpy::GenName() );
     }
 
-  // Flush other objects
-  for ( id_obj = myObjects.begin(); id_obj != myObjects.end(); ++id_obj )
-    if ( ! id_obj->second.IsNull() )
-      id_obj->second->Flush();
+  // Flush other objects. 2 times, for objects depending on Flush() of later created objects
+  std::list< Handle(_pyObject) >::reverse_iterator robj = myOrderedObjects.rbegin();
+  for ( ; robj != myOrderedObjects.rend(); ++robj )
+    if ( ! robj->IsNull() )
+      (*robj)->Flush();
+  std::list< Handle(_pyObject) >::iterator obj = myOrderedObjects.begin();
+  for ( ; obj != myOrderedObjects.end(); ++obj )
+    if ( ! obj->IsNull() )
+      (*obj)->Flush();
 
   myLastCommand->SetOrderNb( ++myNbCommands );
   myCommands.push_back( myLastCommand );
@@ -1137,23 +1172,23 @@ void _pyGen::Flush()
 
 void _pyGen::PlaceSubmeshAfterItsCreation( Handle(_pyCommand) theCmdUsingSubmesh ) const
 {
-  map< _pyID, Handle(_pyObject) >::const_iterator id_obj = myObjects.begin();
-  for ( ; id_obj != myObjects.end(); ++id_obj )
-  {
-    if ( !id_obj->second->IsKind( STANDARD_TYPE( _pySubMesh ))) continue;
-    for ( int iArg = theCmdUsingSubmesh->GetNbArgs(); iArg; --iArg )
-    {
-      const _pyID& arg = theCmdUsingSubmesh->GetArg( iArg );
-      if ( arg.IsEmpty() || arg.Value( 1 ) == '"' || arg.Value( 1 ) == '\'' )
-        continue;
-      list< _pyID > idList = theCmdUsingSubmesh->GetStudyEntries( arg );
-      list< _pyID >::iterator id = idList.begin();
-      for ( ; id != idList.end(); ++id )
-        if ( id_obj->first == *id )
-          // _pySubMesh::Process() does what we need
-          Handle(_pySubMesh)::DownCast( id_obj->second )->Process( theCmdUsingSubmesh );
-    }
-  }
+  // map< _pyID, Handle(_pyObject) >::const_iterator id_obj = myObjects.begin();
+  // for ( ; id_obj != myObjects.end(); ++id_obj )
+  // {
+  //   if ( !id_obj->second->IsKind( STANDARD_TYPE( _pySubMesh ))) continue;
+  //   for ( int iArg = theCmdUsingSubmesh->GetNbArgs(); iArg; --iArg )
+  //   {
+  //     const _pyID& arg = theCmdUsingSubmesh->GetArg( iArg );
+  //     if ( arg.IsEmpty() || arg.Value( 1 ) == '"' || arg.Value( 1 ) == '\'' )
+  //       continue;
+  //     list< _pyID > idList = theCmdUsingSubmesh->GetStudyEntries( arg );
+  //     list< _pyID >::iterator id = idList.begin();
+  //     for ( ; id != idList.end(); ++id )
+  //       if ( id_obj->first == *id )
+  //         // _pySubMesh::Process() does what we need
+  //         Handle(_pySubMesh)::DownCast( id_obj->second )->Process( theCmdUsingSubmesh );
+  //   }
+  // }
 }
 
 //================================================================================
@@ -1173,9 +1208,15 @@ void _pyGen::ClearCommands()
     if ( !id_hyp->second.IsNull() )
       id_hyp->second->ClearCommands();
 
-  map< _pyID, Handle(_pyObject) >::iterator id_obj = myObjects.begin();
-  for ( ; id_obj != myObjects.end(); ++id_obj )
-    id_obj->second->ClearCommands();
+  // Other objects. 2 times, for objects depending on ClearCommands() of later created objects
+  std::list< Handle(_pyObject) >::reverse_iterator robj = myOrderedObjects.rbegin();
+  for ( ; robj != myOrderedObjects.rend(); ++robj )
+    if ( ! robj->IsNull() )
+      (*robj)->ClearCommands();
+  std::list< Handle(_pyObject) >::iterator obj = myOrderedObjects.begin();
+  for ( ; obj != myOrderedObjects.end(); ++obj )
+    if ( ! obj->IsNull() )
+      (*obj)->ClearCommands();
 }
 
 //================================================================================
@@ -1208,6 +1249,8 @@ void _pyGen::Free()
   myHypos.clear();
 
   myFile2ExportedMesh.clear();
+
+  //myKeepAgrCmdsIDs.Print();
 }
 
 //================================================================================
@@ -1468,20 +1511,27 @@ _pyID _pyGen::GenerateNewID( const _pyID& theID )
  */
 //================================================================================
 
-void _pyGen::AddObject( Handle(_pyObject)& theObj )
+bool _pyGen::AddObject( Handle(_pyObject)& theObj )
 {
-  if ( theObj.IsNull() ) return;
+  if ( theObj.IsNull() ) return false;
 
   CheckObjectIsReCreated( theObj );
 
-  if ( theObj->IsKind( STANDARD_TYPE( _pyMesh )))
-    myMeshes.insert( make_pair( theObj->GetID(), Handle(_pyMesh)::DownCast( theObj )));
-
-  else if ( theObj->IsKind( STANDARD_TYPE( _pyMeshEditor )))
-    myMeshEditors.insert( make_pair( theObj->GetID(), Handle(_pyMeshEditor)::DownCast( theObj )));
+  bool add;
 
-  else
-    myObjects.insert( make_pair( theObj->GetID(), theObj ));
+  if ( theObj->IsKind( STANDARD_TYPE( _pyMesh ))) {
+    add = myMeshes.insert( make_pair( theObj->GetID(),
+                                      Handle(_pyMesh)::DownCast( theObj ))).second;
+  }
+  else if ( theObj->IsKind( STANDARD_TYPE( _pyMeshEditor ))) {
+    add = myMeshEditors.insert( make_pair( theObj->GetID(),
+                                          Handle(_pyMeshEditor)::DownCast( theObj ))).second;
+  }
+  else {
+    add = myObjects.insert( make_pair( theObj->GetID(), theObj )).second;
+    if ( add ) myOrderedObjects.push_back( theObj );
+  }
+  return add;
 }
 
 //================================================================================
@@ -1952,7 +2002,7 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand )
         {
           addCmd = *cmd;
           cmd    = addHypCmds.erase( cmd );
-          if ( !theGen->IsToKeepAllCommands() ) {
+          if ( !theGen->IsToKeepAllCommands() && CanClear() ) {
             addCmd->Clear();
             theCommand->Clear();
           }
@@ -3413,7 +3463,7 @@ bool _pyAlgorithm::Addition2Creation( const Handle(_pyCommand)& theCmd,
  */
 //================================================================================
 
-int _pyCommand::GetBegPos( int thePartIndex )
+int _pyCommand::GetBegPos( int thePartIndex ) const
 {
   if ( IsEmpty() )
     return EMPTY;
@@ -3453,7 +3503,7 @@ TCollection_AsciiString _pyCommand::GetIndentation()
     GetWord( myString, end, true );
   else
     end = GetBegPos( RESULT_IND );
-  return myString.SubString( 1, end - 1 );
+  return myString.SubString( 1, Max( end - 1, 1 ));
 }
 
 //================================================================================
@@ -3493,16 +3543,8 @@ const TCollection_AsciiString & _pyCommand::GetResultValue()
 
 int _pyCommand::GetNbResultValues()
 {
-  int nb     = 0;
-  int begPos = 1;
-  int endPos = myString.Location( "=", 1, Length() );
-  while ( begPos < endPos )
-  {
-    _AString str = GetWord( myString, begPos, true );
-    begPos = begPos+ str.Length();
-    nb++;
-  }
-  return (nb-1);
+  GetResultValue(1);
+  return myResults.Length();
 }
 
 
@@ -3513,32 +3555,38 @@ int _pyCommand::GetNbResultValues()
  * \retval const TCollection_AsciiString & - ResultValue with res index substring
  */
 //================================================================================
-TCollection_AsciiString _pyCommand::GetResultValue(int res)
+const _AString& _pyCommand::GetResultValue(int res)
 {
-  int begPos = 1;
-  if ( SkipSpaces( myString, begPos ) && myString.Value( begPos ) == '[' )
-    ++begPos; // skip [, else the whole list is returned
-  int endPos = myString.Location( "=", 1, Length() );
-  int Nb=0;
-  while ( begPos < endPos) {
-    _AString result = GetWord( myString, begPos, true );
-    begPos = begPos + result.Length();
-    Nb++;
-    if(res == Nb) {
-      result.RemoveAll('[');
-      result.RemoveAll(']');
-      return result;
+  if ( GetResultValue().IsEmpty() )
+    return theEmptyString;
+
+  if ( myResults.IsEmpty() )
+  {
+    int begPos = 1;
+    if ( SkipSpaces( myRes, begPos ) && myRes.Value( begPos ) == '[' )
+      ++begPos; // skip [, else the whole list is returned
+    while ( begPos < myRes.Length() ) {
+      _AString result = GetWord( myRes, begPos, true );
+      begPos += result.Length();
+      // if(res == Nb) {
+      //   result.RemoveAll('[');
+      //   result.RemoveAll(']');
+      //   return result;
+      // }
+      // if(Nb>res)
+      //   break;
+      myResults.Append( result );
     }
-    if(Nb>res)
-      break;
   }
+  if ( res > 0 && res <= myResults.Length() )
+    return myResults( res );
   return theEmptyString;
 }
 
 //================================================================================
 /*!
  * \brief Return substring of python command looking like ResVal = Object.Meth()
 * \retval const TCollection_AsciiString & - Object substring
+ * \retval const TCollection_AsciiString & - Object substring
  */
 //================================================================================
 
@@ -3624,6 +3672,20 @@ const TCollection_AsciiString & _pyCommand::GetMethod()
   return myMeth;
 }
 
+//================================================================================
+/*!
+ * \brief Returns true if there are brackets after the method
+ */
+//================================================================================
+
+bool _pyCommand::IsMethodCall()
+{
+  if ( GetMethod().IsEmpty() )
+    return false;
+  const char* s = myString.ToCString() + GetBegPos( METHOD_IND ) + myMeth.Length() - 1;
+  return ( s[0] == '(' || s[1] == '(' );
+}
+
 //================================================================================
 /*!
  * \brief Return substring of python command looking like ResVal = Obj.Meth(Arg1,...)
@@ -3707,6 +3769,24 @@ const TCollection_AsciiString & _pyCommand::GetArg( int index )
   return myArgs( index );
 }
 
+//================================================================================
+/*!
+ * \brief Return position where arguments begin
+ */
+//================================================================================
+
+int _pyCommand::GetArgBeginning() const
+{
+  int pos = GetBegPos( ARG1_IND );
+  if ( pos == UNKNOWN )
+  {
+    pos = GetBegPos( METHOD_IND ) + myMeth.Length();
+    if ( pos < 1 )
+      pos = myString.Location( "(", 4, Length() ); // 4 = strlen("b.c(")
+  }
+  return pos;
+}
+
 //================================================================================
 /*!
  * \brief Check if char is a word part
@@ -3810,6 +3890,26 @@ bool _pyCommand::IsStudyEntry( const TCollection_AsciiString& str )
   return nbColons > 2 && str.Length()-nbColons > 2;
 }
 
+//================================================================================
+/*!
+ * \brief Returns true if the string looks like an object ID but not like a list,
+ *        string, command etc.
+ */
+//================================================================================
+
+bool _pyCommand::IsID( const TCollection_AsciiString& str )
+{
+  if ( str.Length() < 1 ) return false;
+
+  const char* s = str.ToCString();
+
+  for ( int i = 0; i < str.Length(); ++i )
+    if ( !IsIDChar( s[i] ))
+      return false;
+
+  return true;
+}
+
 //================================================================================
 /*!
  * \brief Finds entries in a sting
@@ -3971,9 +4071,9 @@ void _pyCommand::Comment()
     myString.Insert( i, "#" );
     for ( int iPart = 1; iPart <= myBegPos.Length(); ++iPart )
     {
-      int begPos = GetBegPos( iPart + 1 );
-      if ( begPos != UNKNOWN )
-        SetBegPos( iPart + 1, begPos + 1 );
+      int begPos = GetBegPos( iPart );
+      if ( begPos != UNKNOWN && begPos != EMPTY )
+        SetBegPos( iPart, begPos + 1 );
     }
   }
 }
@@ -4021,7 +4121,8 @@ bool _pyCommand::AddAccessorMethod( _pyID theObjectID, const char* theAcsMethod
     // check that theObjectID is not just a part of a longer ID
     int afterEnd = beg + theObjectID.Length();
     Standard_Character c = myString.Value( afterEnd );
-    if ( !isalnum( c ) && c != ':' ) {
+    if ( !IsIDChar( c ))
+    {
       // check if accessor method already present
       if ( c != '.' ||
            myString.Location( (char*) theAcsMethod, afterEnd, Length() ) != afterEnd+1) {
@@ -4038,7 +4139,7 @@ bool _pyCommand::AddAccessorMethod( _pyID theObjectID, const char* theAcsMethod
         added = true;
       }
     }
-    beg = afterEnd; // is a part - next search
+    beg = afterEnd; // is a part -> next search
   }
   return added;
 }
@@ -4113,7 +4214,8 @@ _pyID _pyObject::FatherID(const _pyID & childID)
 
 //================================================================================
 /*!
- * \brief SelfEraser erases creation command if no more it's commands invoked
+ * \brief SelfEraser erases creation command if none of it's commands invoked
+ *        (e.g. filterManager) or it's not used as a command argument (e.g. a filter)
  */
 //================================================================================
 
@@ -4126,11 +4228,12 @@ _pySelfEraser::_pySelfEraser(const Handle(_pyCommand)& theCreationCmd)
 
 //================================================================================
 /*!
- * \brief SelfEraser erases creation command if no more it's commands invoked
+ * \brief SelfEraser erases creation command if none of it's commands invoked
+ *        (e.g. filterManager) or it's not used as a command argument (e.g. a filter)
  */
 //================================================================================
 
-void _pySelfEraser::Flush()
+bool _pySelfEraser::CanClear()
 {
   bool toErase = false;
   if ( myIgnoreOwnCalls ) // check if this obj is used as argument
@@ -4138,23 +4241,58 @@ void _pySelfEraser::Flush()
     int nbArgUses = 0;
     list< Handle(_pyCommand) >::iterator cmd = myArgCmds.begin();
     for ( ; cmd != myArgCmds.end(); ++cmd )
-      nbArgUses += !(*cmd)->IsEmpty();
+      nbArgUses += IsAliveCmd( *cmd );
+
     toErase = ( nbArgUses < 1 );
   }
   else
   {
-    int nbCalls = GetNbCalls();
-    if ( nbCalls > 0 )
-    {
-      // ignore cleared commands
-      std::list< Handle(_pyCommand) >& cmds = GetProcessedCmds();
-      std::list< Handle(_pyCommand) >::const_iterator cmd = cmds.begin();
-      for ( ; cmd != cmds.end(); ++cmd )
-        nbCalls -= (*cmd)->IsEmpty();
-    }
+    int nbCalls = 0;
+    std::list< Handle(_pyCommand) >& cmds = GetProcessedCmds();
+    std::list< Handle(_pyCommand) >::iterator cmd = cmds.begin();
+    for ( ; cmd != cmds.end();  )
+      // check of cmd emptiness is not enough as object can change
+      if (( *cmd )->GetString().Search( GetID() ) > 0 )
+        ++nbCalls, ++cmd;
+      else
+        cmd = cmds.erase( cmd ); // save the cmd from clearing
+
     toErase = ( nbCalls < 1 );
   }
-  if ( toErase )
+  return toErase;
+}
+
+//================================================================================
+/*!
+ * \brief Check if a command is or can be cleared
+ */
+//================================================================================
+
+bool _pySelfEraser::IsAliveCmd( const Handle(_pyCommand)& theCmd )
+{
+  if ( theCmd->IsEmpty() )
+    return false;
+
+  if ( !theGen->IsToKeepAllCommands() )
+  {
+    const _pyID& objID = theCmd->GetObject();
+    Handle( _pyObject ) obj = theGen->FindObject( objID );
+    if ( !obj.IsNull() )
+      return !obj->CanClear();
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief SelfEraser erases creation command if none of it's commands invoked
+ *        (e.g. filterManager) or it's not used as a command argument (e.g. a filter)
+ */
+//================================================================================
+
+void _pySelfEraser::Flush()
+{
+  if ( CanClear() )
   {
     myIsPublished = false;
     _pyObject::ClearCommands();
@@ -4167,11 +4305,12 @@ void _pySelfEraser::Flush()
  */
 //================================================================================
 
-_pySubMesh::_pySubMesh(const Handle(_pyCommand)& theCreationCmd):
+_pySubMesh::_pySubMesh(const Handle(_pyCommand)& theCreationCmd, bool toKeepAgrCmds):
   _pyObject(theCreationCmd)
 {
   myMesh = ObjectToMesh( theGen->FindObject( theCreationCmd->GetObject() ));
-  theGen->KeepAgrCmds( GetID() ); // ask to fill myArgCmds
+  if ( toKeepAgrCmds )
+    theGen->KeepAgrCmds( GetID() ); // ask to fill myArgCmds
 }
 
 //================================================================================
@@ -4257,7 +4396,7 @@ void _pySubMesh::Flush()
 //================================================================================
 
 _pyGroup::_pyGroup(const Handle(_pyCommand)& theCreationCmd, const _pyID & id)
-  :_pySubMesh(theCreationCmd)
+  :_pySubMesh(theCreationCmd, /*toKeepAgrCmds=*/false)
 {
   if ( !id.IsEmpty() )
     setID( id );
@@ -4512,6 +4651,8 @@ void _pyFilter::Process( const Handle(_pyCommand)& theCommand)
     GetCreationCmd()->GetString() = theCommand->GetString();
     theCommand->Clear();
     theCommand->AddDependantCmd( GetCreationCmd() );
+    // why swap? -- it's needed
+    //GetCreationCmd()->Clear();
   }
   else if ( theCommand->GetMethod() == "SetMesh" )
   {
@@ -4734,3 +4875,219 @@ _pyHypothesisReader::GetHypothesis(const _AString&           hypType,
   }
   return resHyp;
 }
+
+//================================================================================
+/*!
+ * \brief Adds an object ID to some family of IDs with a common prefix
+ *  \param [in] str - the object ID
+ *  \return bool - \c false if \a str does not have the same prefix as \a this family
+ *          (for internal usage)
+ */
+//================================================================================
+
+bool _pyStringFamily::Add( const char* str )
+{
+  if ( strncmp( str, _prefix.ToCString(), _prefix.Length() ) != 0 )
+    return false; // expected prefix is missing
+
+  str += _prefix.Length(); // skip _prefix
+
+  // try to add to some of child falimies
+  std::list< _pyStringFamily >::iterator itSub = _subFams.begin();
+  for ( ; itSub != _subFams.end(); ++itSub )
+    if ( itSub->Add( str ))
+      return true;
+
+  // no suitable family found - add str to _strings or create a new child family
+
+  // look for a proper place within sorted _strings
+  std::list< _AString >::iterator itStr = _strings.begin();
+  while ( itStr != _strings.end() && itStr->IsLess( str ))
+    ++itStr;
+  if ( itStr != _strings.end() && itStr->IsEqual( str ))
+    return true; // same ID already kept
+
+  const int minPrefixSize = 4;
+
+  // count "smaller" strings with the same prefix
+  std::list< _AString >::iterator itLess = itStr; --itLess;
+  int nbLess = 0;
+  for ( ; itLess != _strings.end(); --itLess )
+    if ( strncmp( str, itLess->ToCString(), minPrefixSize ) == 0 )
+      ++nbLess;
+    else
+      break;
+  ++itLess;
+  // count "greater" strings with the same prefix
+  std::list< _AString >::iterator itMore = itStr;
+  int nbMore = 0;
+  for ( ; itMore != _strings.end(); ++itMore )
+    if ( strncmp( str, itMore->ToCString(), minPrefixSize ) == 0 )
+      ++nbMore;
+    else
+      break;
+  --itMore;
+  if ( nbLess + nbMore > 1 ) // ------- ADD a NEW CHILD FAMILY -------------
+  {
+    // look for a maximal prefix length
+    // int lessPrefSize = 3, morePrefSize = 3;
+    // if ( nbLess > 0 )
+    //   while( itLess->ToCString()[ lessPrefSize ] == str[ lessPrefSize ]  )
+    //     ++lessPrefSize;
+    // if ( nbMore > 0 )
+    //   while ( itMore->ToCString()[ morePrefSize ] == str[ morePrefSize ] )
+    //     ++morePrefSize;
+    // int prefixSize = 3;
+    // if ( nbLess == 0 )
+    //   prefixSize = morePrefSize;
+    // else if ( nbMore == 0 )
+    //   prefixSize = lessPrefSize;
+    // else
+    //   prefixSize = Min( lessPrefSize, morePrefSize );
+    int prefixSize = minPrefixSize;
+    _AString newPrefix ( str, prefixSize );
+
+    // look for a proper place within _subFams sorted by _prefix
+    for ( itSub = _subFams.begin(); itSub != _subFams.end(); ++itSub )
+      if ( !itSub->_prefix.IsLess( newPrefix ))
+        break;
+
+    // add the new _pyStringFamily
+    itSub = _subFams.insert( itSub, _pyStringFamily());
+    _pyStringFamily& newSubFam = *itSub;
+    newSubFam._prefix = newPrefix;
+
+    // pass this->_strings to newSubFam._strings
+    for ( itStr = itLess; nbLess > 0; --nbLess, ++itStr )
+      newSubFam._strings.push_back( itStr->ToCString() + prefixSize );
+    newSubFam._strings.push_back( str + prefixSize );
+    for ( ; nbMore > 0; --nbMore, ++itStr )
+      newSubFam._strings.push_back( itStr->ToCString() + prefixSize );
+
+    _strings.erase( itLess, ++itMore );
+  }
+  else // to few string to make a family fot them
+  {
+    _strings.insert( itStr, str );
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Finds an object ID in the command
+ *  \param [in] longStr - the command string
+ *  \param [out] subStr - the found object ID
+ *  \return bool - \c true if the object ID found
+ */
+//================================================================================
+
+bool _pyStringFamily::IsInArgs( Handle( _pyCommand)& cmd, std::list<_AString>& subStr )
+{
+  const _AString& longStr = cmd->GetString();
+  const char*           s = longStr.ToCString();
+
+  // look in _subFams
+  std::list< _pyStringFamily >::iterator itSub = _subFams.begin();
+  int nbFound = 0, pos, len, from, argBeg = cmd->GetArgBeginning();
+  if ( argBeg < 4 || argBeg > longStr.Length() )
+    return false;
+  for ( ; itSub != _subFams.end(); ++itSub )
+  {
+    from = argBeg;
+    while (( pos = longStr.Location( itSub->_prefix, from, longStr.Length() )))
+      if (( len = itSub->isIn( s + pos-1 + itSub->_prefix.Length() )) >= 0 )
+      {
+        subStr.push_back( _AString( s + pos-1, len + itSub->_prefix.Length() ));
+        from = pos + len + itSub->_prefix.Length();
+        nbFound++;
+      }
+      else
+      {
+        from += itSub->_prefix.Length();
+      }
+  }
+  // look among _strings
+  std::list< _AString >::iterator itStr = _strings.begin();
+  for ( ; itStr != _strings.end(); ++itStr )
+    if (( pos = longStr.Location( *itStr, argBeg, longStr.Length() )))
+      // check that object ID does not continue after len
+      if ( !cmd->IsIDChar( s[ pos + itStr->Length() - 1 ] ))
+      {
+        subStr.push_back( *itStr );
+        nbFound++;
+      }
+  return nbFound;
+}
+
+//================================================================================
+/*!
+ * \brief Return remainder length of the object ID after my _prefix
+ *  \param [in] str - remainder of the command after my _prefix
+ *  \return int - length of the object ID or -1 if not found
+ */
+//================================================================================
+
+int _pyStringFamily::isIn( const char* str )
+{
+  std::list< _pyStringFamily >::iterator itSub = _subFams.begin();
+  int len = -1;
+  for ( ; itSub != _subFams.end(); ++itSub )
+  {
+    int cmp = strncmp( str, itSub->_prefix.ToCString(), itSub->_prefix.Length() );
+    if ( cmp == 0 )
+    {
+      if (( len = itSub->isIn( str + itSub->_prefix.Length() )) >= 0 )
+        return itSub->_prefix.Length() + len;
+    }
+    else if ( cmp > 0 )
+      break;
+  }
+  if ( !_strings.empty() )
+  {
+    std::list< _AString >::iterator itStr = _strings.begin();
+    bool firstEmpty = itStr->IsEmpty();
+    if ( firstEmpty )
+      ++itStr, len = 0;
+    for ( ; itStr != _strings.end(); ++itStr )
+    {
+      int cmp = strncmp( str, itStr->ToCString(), itStr->Length() );
+      if ( cmp == 0 )
+      {
+        len = itStr->Length();
+        break;
+      }
+      else if ( cmp < 0 )
+      {
+        break;
+      }
+    }
+
+    // check that object ID does not continue after len
+    if ( len >= 0 && _pyCommand::IsIDChar( str[len] ))
+      len = -1;
+  }
+
+  return len;
+}
+
+//================================================================================
+/*!
+ * \brief DEBUG
+ */
+//================================================================================
+
+void _pyStringFamily::Print( int level )
+{
+  cout << string( level, ' ' ) << "prefix = '" << _prefix << "' : ";
+  std::list< _AString >::iterator itStr = _strings.begin();
+  for ( ; itStr != _strings.end(); ++itStr )
+    cout << *itStr << " | ";
+  cout << endl;
+  std::list< _pyStringFamily >::iterator itSub = _subFams.begin();
+  for ( ; itSub != _subFams.end(); ++itSub )
+    itSub->Print( level + 1 );
+  if ( level == 0 )
+    cout << string( 70, '-' ) << endl;
+}
+