+
+//================================================================================
+/*!
+ * \brief Creates _pyGroup
+ */
+//================================================================================
+
+_pyGroup::_pyGroup(const Handle(_pyCommand)& theCreationCmd, const _pyID & id)
+ :_pySubMesh(theCreationCmd, /*toKeepAgrCmds=*/false)
+{
+ if ( !id.IsEmpty() )
+ setID( id );
+
+ myCanClearCreationCmd = true;
+
+ const _AString& method = theCreationCmd->GetMethod();
+ if ( method == "CreateGroup" ) // CreateGroup() --> CreateEmptyGroup()
+ {
+ theCreationCmd->SetMethod( "CreateEmptyGroup" );
+ }
+ // ----------------------------------------------------------------------
+ else if ( method == "CreateGroupFromGEOM" ) // (type, name, grp)
+ {
+ _pyID geom = theCreationCmd->GetArg( 3 );
+ // VSR 24/12/2010. PAL21106: always use GroupOnGeom() function on dump
+ // next if(){...} section is commented
+ //if ( sameGroupType( geom, theCreationCmd->GetArg( 1 )) ) { // --> Group(geom)
+ // theCreationCmd->SetMethod( "Group" );
+ // theCreationCmd->RemoveArgs();
+ // theCreationCmd->SetArg( 1, geom );
+ //}
+ //else {
+ // ------------------------->>>>> GroupOnGeom( geom, name, typ )
+ _pyID type = theCreationCmd->GetArg( 1 );
+ _pyID name = theCreationCmd->GetArg( 2 );
+ theCreationCmd->SetMethod( "GroupOnGeom" );
+ theCreationCmd->RemoveArgs();
+ theCreationCmd->SetArg( 1, geom );
+ theCreationCmd->SetArg( 2, name );
+ theCreationCmd->SetArg( 3, type );
+ //}
+ }
+ else if ( method == "CreateGroupFromFilter" )
+ {
+ // -> GroupOnFilter(typ, name, aFilter0x4743dc0 -> aFilter_1)
+ theCreationCmd->SetMethod( "GroupOnFilter" );
+
+ _pyID filterID = theCreationCmd->GetArg(3);
+ Handle(_pyFilter) filter = Handle(_pyFilter)::DownCast( theGen->FindObject( filterID ));
+ if ( !filter.IsNull())
+ {
+ if ( !filter->GetNewID().IsEmpty() )
+ theCreationCmd->SetArg( 3, filter->GetNewID() );
+ //filter->AddUser( this );
+ }
+ myFilter = filter;
+ }
+ else if ( method == "GetGroups" )
+ {
+ myCanClearCreationCmd = ( theCreationCmd->GetNbResultValues() == 1 );
+ }
+ else
+ {
+ // theCreationCmd does something else apart from creation of this group
+ // and thus it can't be cleared if this group is removed
+ myCanClearCreationCmd = false;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Check if "[ group1, group2 ] = mesh.GetGroups()" creation command
+ * can be cleared
+ */
+//================================================================================
+
+bool _pyGroup::CanClear()
+{
+ if ( IsInStudy() )
+ return false;
+
+ if ( !myCanClearCreationCmd &&
+ !myCreationCmd.IsNull() &&
+ myCreationCmd->GetMethod() == "GetGroups" )
+ {
+ TCollection_AsciiString grIDs = myCreationCmd->GetResultValue();
+ list< _pyID > idList = myCreationCmd->GetStudyEntries( grIDs );
+ list< _pyID >::iterator grID = idList.begin();
+ if ( GetID() == *grID )
+ {
+ myCanClearCreationCmd = true;
+ list< Handle(_pyGroup ) > groups;
+ for ( ; grID != idList.end(); ++grID )
+ {
+ Handle(_pyGroup) group = Handle(_pyGroup)::DownCast( theGen->FindObject( *grID ));
+ if ( group.IsNull() ) continue;
+ groups.push_back( group );
+ if ( group->IsInStudy() )
+ myCanClearCreationCmd = false;
+ }
+ // set myCanClearCreationCmd == true to all groups
+ list< Handle(_pyGroup ) >::iterator group = groups.begin();
+ for ( ; group != groups.end(); ++group )
+ (*group)->myCanClearCreationCmd = myCanClearCreationCmd;
+ }
+ }
+
+ return myCanClearCreationCmd;
+}
+
+//================================================================================
+/*!
+ * \brief set myCanClearCreationCmd = true if the main action of the creation
+ * command is discarded
+ */
+//================================================================================
+
+void _pyGroup::RemovedWithContents()
+{
+ // this code would be appropriate if Add0DElementsToAllNodes() returned only new nodes
+ // via a created group
+ //if ( GetCreationCmd()->GetMethod() == "Add0DElementsToAllNodes")
+ // myCanClearCreationCmd = true;
+}
+
+//================================================================================
+/*!
+ * \brief To convert creation of a group by filter
+ */
+//================================================================================
+
+void _pyGroup::Process( const Handle(_pyCommand)& theCommand)
+{
+ // Convert the following set of commands into mesh.MakeGroupByFilter(groupName, theFilter)
+ // group = mesh.CreateEmptyGroup( elemType, groupName )
+ // aFilter.SetMesh(mesh)
+ // nbAdd = group.AddFrom( aFilter )
+ Handle(_pyFilter) filter;
+ if ( theCommand->GetMethod() == "AddFrom" )
+ {
+ _pyID idSource = theCommand->GetArg(1);
+ // check if idSource is a filter
+ filter = Handle(_pyFilter)::DownCast( theGen->FindObject( idSource ));
+ if ( !filter.IsNull() )
+ {
+ // find aFilter.SetMesh(mesh) to clear it, it should be just before theCommand
+ list< Handle(_pyCommand) >::reverse_iterator cmdIt = theGen->GetCommands().rbegin();
+ while ( *cmdIt != theCommand ) ++cmdIt;
+ while ( (*cmdIt)->GetOrderNb() != 1 )
+ {
+ const Handle(_pyCommand)& setMeshCmd = *(++cmdIt);
+ if ((setMeshCmd->GetObject() == idSource ||
+ setMeshCmd->GetObject() == filter->GetNewID() )
+ &&
+ setMeshCmd->GetMethod() == "SetMesh")
+ {
+ setMeshCmd->Clear();
+ break;
+ }
+ }
+ // replace 3 commands by one
+ theCommand->Clear();
+ const Handle(_pyCommand)& makeGroupCmd = GetCreationCmd();
+ TCollection_AsciiString name = makeGroupCmd->GetArg( 2 );
+ if ( !filter->GetNewID().IsEmpty() )
+ idSource = filter->GetNewID();
+ makeGroupCmd->SetMethod( "MakeGroupByFilter" );
+ makeGroupCmd->SetArg( 1, name );
+ makeGroupCmd->SetArg( 2, idSource );
+ filter->AddArgCmd( makeGroupCmd );
+ }
+ }
+ else if ( theCommand->GetMethod() == "SetFilter" )
+ {
+ // set new name of a filter or clear the command if the same filter is set
+ _pyID filterID = theCommand->GetArg(1);
+ filter = Handle(_pyFilter)::DownCast( theGen->FindObject( filterID ));
+ if ( !myFilter.IsNull() && filter == myFilter )
+ theCommand->Clear();
+ else if ( !filter.IsNull() && !filter->GetNewID().IsEmpty() )
+ theCommand->SetArg( 1, filter->GetNewID() );
+ myFilter = filter;
+ }
+ else if ( theCommand->GetMethod() == "GetFilter" )
+ {
+ // GetFilter() returns a filter with other ID, make myFilter process
+ // calls of the returned filter
+ if ( !myFilter.IsNull() )
+ {
+ theGen->SetProxyObject( theCommand->GetResultValue(), myFilter );
+ theCommand->Clear();
+ }
+ }
+
+ // if ( !filter.IsNull() )
+ // filter->AddUser( this );
+
+ theGen->AddMeshAccessorMethod( theCommand );
+}
+
+//================================================================================
+/*!
+ * \brief Prevent clearing "DoubleNode...() command if a group created by it is removed
+ */
+//================================================================================
+
+void _pyGroup::Flush()
+{
+ if ( !theGen->IsToKeepAllCommands() &&
+ !myCreationCmd.IsNull() && !myCanClearCreationCmd )
+ {
+ myCreationCmd.Nullify(); // this way myCreationCmd won't be cleared
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Constructor of _pyFilter
+ */
+//================================================================================
+
+_pyFilter::_pyFilter(const Handle(_pyCommand)& theCreationCmd, const _pyID& newID/*=""*/)
+ :_pyObject(theCreationCmd), myNewID( newID )
+{
+ //myIsPublished = true; // prevent clearing as a not published
+ theGen->KeepAgrCmds( GetID() ); // ask to fill myArgCmds
+}
+
+//================================================================================
+/*!
+ * \brief To convert creation of a filter by criteria and
+ * to replace an old name by a new one
+ */
+//================================================================================
+
+void _pyFilter::Process( const Handle(_pyCommand)& theCommand)
+{
+ if ( theCommand->GetObject() == GetID() )
+ _pyObject::Process(theCommand); // count commands
+
+ if ( !myNewID.IsEmpty() )
+ theCommand->SetObject( myNewID );
+
+ // Convert the following set of commands into smesh.GetFilterFromCriteria(criteria)
+ // aFilter0x2aaab0487080 = aFilterManager.CreateFilter()
+ // aFilter0x2aaab0487080.SetCriteria(aCriteria)
+ if ( GetNbCalls() == 1 && // none method was called before this SetCriteria() call
+ theCommand->GetMethod() == "SetCriteria")
+ {
+ // aFilter.SetCriteria(aCriteria) ->
+ // aFilter = smesh.GetFilterFromCriteria(criteria)
+ if ( myNewID.IsEmpty() )
+ theCommand->SetResultValue( GetID() );
+ else
+ theCommand->SetResultValue( myNewID );
+ theCommand->SetObject( SMESH_2smeshpy::GenName() );
+ theCommand->SetMethod( "GetFilterFromCriteria" );
+
+ // Swap "aFilterManager.CreateFilter()" and "smesh.GetFilterFromCriteria(criteria)"
+ GetCreationCmd()->Clear();
+ GetCreationCmd()->GetString() = theCommand->GetString();
+ theCommand->Clear();
+ theCommand->AddDependantCmd( GetCreationCmd() );
+ // why swap? -- it's needed
+ //GetCreationCmd()->Clear();
+ }
+ else if ( theCommand->GetMethod() == "SetMesh" )
+ {
+ if ( myMesh == theCommand->GetArg( 1 ))
+ theCommand->Clear();
+ else
+ myMesh = theCommand->GetArg( 1 );
+ theGen->AddMeshAccessorMethod( theCommand );
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Set new filter name to the creation command and to myArgCmds
+ */
+//================================================================================
+
+void _pyFilter::Flush()
+{
+ if ( myNewID.IsEmpty() ) return;
+
+ list< Handle(_pyCommand) >::iterator cmd = myArgCmds.begin();
+ for ( ; cmd != myArgCmds.end(); ++cmd )
+ if ( !(*cmd)->IsEmpty() )
+ {
+ _AString cmdStr = (*cmd)->GetString();
+ _AString id = GetID();
+ int pos = cmdStr.Search( id );
+ if ( pos > 0 )
+ {
+ cmdStr.Remove( pos, id.Length() );
+ cmdStr.Insert( pos, myNewID );
+ }
+ (*cmd)->Clear();
+ (*cmd)->GetString() = cmdStr;
+ }
+
+ if ( !GetCreationCmd()->IsEmpty() )
+ GetCreationCmd()->SetResultValue( myNewID );
+}
+
+//================================================================================
+/*!
+ * \brief Return true if all my users can be cleared
+ */
+//================================================================================
+
+bool _pyObject::CanClear()
+{
+ list< Handle(_pyCommand) >::iterator cmd = myArgCmds.begin();
+ for ( ; cmd != myArgCmds.end(); ++cmd )
+ if ( !(*cmd)->IsEmpty() )
+ {
+ Handle(_pyObject) obj = theGen->FindObject( (*cmd)->GetObject() );
+ if ( !obj.IsNull() && !obj->CanClear() )
+ return false;
+ }
+ return ( !myIsPublished );
+}
+
+//================================================================================
+/*!
+ * \brief Reads _pyHypothesis'es from resource files of mesher Plugins
+ */
+//================================================================================
+
+_pyHypothesisReader::_pyHypothesisReader()
+{
+ // Read xml files
+ vector< string > xmlPaths = SMESH_Gen::GetPluginXMLPaths();
+ LDOMParser xmlParser;
+ for ( size_t i = 0; i < xmlPaths.size(); ++i )
+ {
+ bool error = xmlParser.parse( xmlPaths[i].c_str() );
+ if ( error )
+ {
+ _AString data;
+ INFOS( xmlParser.GetError(data) );
+ continue;
+ }
+ // <algorithm type="Regular_1D"
+ // label-id="Wire discretisation"
+ // ...>
+ // <python-wrap>
+ // <algo>Regular_1D=Segment()</algo>
+ // <hypo>LocalLength=LocalLength(SetLength(1),,SetPrecision(1))</hypo>
+ //
+ LDOM_Document xmlDoc = xmlParser.getDocument();
+ LDOM_NodeList algoNodeList = xmlDoc.getElementsByTagName( "algorithm" );
+ for ( int i = 0; i < algoNodeList.getLength(); ++i )
+ {
+ LDOM_Node algoNode = algoNodeList.item( i );
+ LDOM_Element& algoElem = (LDOM_Element&) algoNode;
+ LDOM_NodeList pyAlgoNodeList = algoElem.getElementsByTagName( "algo" );
+ if ( pyAlgoNodeList.getLength() < 1 ) continue;
+
+ _AString text, algoType, method, arg;
+ for ( int iA = 0; iA < pyAlgoNodeList.getLength(); ++iA )
+ {
+ LDOM_Node pyAlgoNode = pyAlgoNodeList.item( iA );
+ LDOM_Node textNode = pyAlgoNode.getFirstChild();
+ text = textNode.getNodeValue();
+ Handle(_pyCommand) algoCmd = new _pyCommand( text );
+ algoType = algoCmd->GetResultValue();
+ method = algoCmd->GetMethod();
+ arg = algoCmd->GetArg(1);
+ if ( !algoType.IsEmpty() && !method.IsEmpty() )
+ {
+ Handle(_pyAlgorithm) algo = new _pyAlgorithm( algoCmd );
+ algo->SetConvMethodAndType( method, algoType );
+ if ( !arg.IsEmpty() )
+ algo->setCreationArg( 1, arg );
+
+ myType2Hyp[ algoType ] = algo;
+ break;
+ }
+ }
+ if ( algoType.IsEmpty() ) continue;
+
+ LDOM_NodeList pyHypoNodeList = algoElem.getElementsByTagName( "hypo" );
+ _AString hypType;
+ Handle( _pyHypothesis ) hyp;
+ for ( int iH = 0; iH < pyHypoNodeList.getLength(); ++iH )
+ {
+ LDOM_Node pyHypoNode = pyHypoNodeList.item( iH );
+ LDOM_Node textNode = pyHypoNode.getFirstChild();
+ text = textNode.getNodeValue();
+ Handle(_pyCommand) hypoCmd = new _pyCommand( text );
+ hypType = hypoCmd->GetResultValue();
+ method = hypoCmd->GetMethod();
+ if ( !hypType.IsEmpty() && !method.IsEmpty() )
+ {
+ map<_AString, Handle(_pyHypothesis)>::iterator type2hyp = myType2Hyp.find( hypType );
+ if ( type2hyp == myType2Hyp.end() )
+ hyp = new _pyHypothesis( hypoCmd );
+ else
+ hyp = type2hyp->second;
+ hyp->SetConvMethodAndType( method, algoType );
+ for ( int iArg = 1; iArg <= hypoCmd->GetNbArgs(); ++iArg )
+ {
+ _pyCommand argCmd( hypoCmd->GetArg( iArg ));
+ _AString argMethod = argCmd.GetMethod();
+ _AString argNbText = argCmd.GetArg( 1 );
+ if ( argMethod.IsEmpty() && !argCmd.IsEmpty() )
+ hyp->setCreationArg( 1, argCmd.GetString() ); // e.g. Parameters(smesh.SIMPLE)
+ else
+ hyp->AddArgMethod( argMethod,
+ argNbText.IsIntegerValue() ? argNbText.IntegerValue() : 1 );
+ }
+ myType2Hyp[ hypType ] = hyp;
+ }
+ }
+ }
+ // <hypothesis type="BLSURF_Parameters"
+ // ...
+ // dim="2">
+ // <python-wrap>
+ // <accumulative-methods>
+ // SetEnforcedVertex,
+ // SetEnforcedVertexNamed
+ // </accumulative-methods>
+ // </python-wrap>
+ // </hypothesis>
+ //
+ LDOM_NodeList hypNodeList = xmlDoc.getElementsByTagName( "hypothesis" );
+ for ( int i = 0; i < hypNodeList.getLength(); ++i )
+ {
+ LDOM_Node hypNode = hypNodeList.item( i );
+ LDOM_Element& hypElem = (LDOM_Element&) hypNode;
+ _AString hypType = hypElem.getAttribute("type");
+ LDOM_NodeList methNodeList = hypElem.getElementsByTagName( "accumulative-methods" );
+ if ( methNodeList.getLength() != 1 || hypType.IsEmpty() ) continue;
+
+ map<_AString, Handle(_pyHypothesis)>::const_iterator type2hyp = myType2Hyp.find( hypType );
+ if ( type2hyp == myType2Hyp.end() ) continue;
+
+ LDOM_Node methNode = methNodeList.item( 0 );
+ LDOM_Node textNode = methNode.getFirstChild();
+ _AString text = textNode.getNodeValue();
+ _AString method;
+ int pos = 1;
+ do {
+ method = _pyCommand::GetWord( text, pos, /*forward= */true );
+ pos += method.Length();
+ type2hyp->second->AddAccumulativeMethod( method );
+ }
+ while ( !method.IsEmpty() );
+ }
+
+ } // loop on xmlPaths
+}
+
+//================================================================================
+/*!
+ * \brief Returns a new hypothesis initialized according to the read information
+ */
+//================================================================================
+
+Handle(_pyHypothesis)
+_pyHypothesisReader::GetHypothesis(const _AString& hypType,
+ const Handle(_pyCommand)& creationCmd) const
+{
+ Handle(_pyHypothesis) resHyp, sampleHyp;
+
+ map<_AString, Handle(_pyHypothesis)>::const_iterator type2hyp = myType2Hyp.find( hypType );
+ if ( type2hyp != myType2Hyp.end() )
+ sampleHyp = type2hyp->second;
+
+ if ( sampleHyp.IsNull() )
+ {
+ resHyp = new _pyHypothesis(creationCmd);
+ }
+ else
+ {
+ if ( sampleHyp->IsAlgo() )
+ resHyp = new _pyAlgorithm( creationCmd );
+ else
+ resHyp = new _pyHypothesis(creationCmd);
+ resHyp->Assign( sampleHyp, _pyID() );
+ }
+ 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;
+}
+