//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1998 // // File: model.cpp // //-------------------------------------------------------------------------- // // MODEL.CPP // #include #include #include "basics.h" #include "algos.h" #include "gmprop.h" #include "model.h" #include "gmobj.h" struct EC_STR { ECGM _ec; SZC _szc; }; static EC_STR vEcToStr [] = { #define GMERRORSTR #include "errordef.h" { EC_OK, "no error" } }; SZC MODEL :: SzcFromEc ( ECGM ec ) { int cEc = UBOUND(vEcToStr); for ( int i = 0; i < cEc; i++ ) { if ( ec == vEcToStr[i]._ec ) return vEcToStr[i]._szc; } return NULL; } // Iterator class for MODELs MODEL::ITER::ITER(MODEL& model, GOBJMBN::EBNOBJ eType) : _eType(eType), _model(model) { Reset(); } MODEL::ITER::ITER(MODEL& model) : _model(model) { } void MODEL::ITER::CreateNodeIterator() { _eType = GOBJMBN::EBNO_NODE; Reset(); } void MODEL::ITER :: Reset () { _pCurrent = NULL; _itsym = _model.Mpsymtbl().begin(); BNext(); } bool MODEL::ITER :: BNext () { while ( _itsym != _model.Mpsymtbl().end() ) { _pCurrent = (*_itsym).second.Pobj(); _zsrCurrent = (*_itsym).first; _itsym++; if ( _pCurrent->EType() == _eType ) return true; } _pCurrent = NULL; return false; } /////////////////////////////////////////////////////////////////////////// // MODEL naming commentary. // // Symbolic names in a belief network come in two types: names which users // can enter (or edit into a DSC file) and those which they cannot. // // The basic (user-definable) symbolic name follows exactly the rules of // standard 'C', except that periods ('.') are allowed inside a name. // // There is a need for generation of names which are clearly distinguishable // from user-definable names; these are called "internal" names. The only // difference is that the legal character set is extended to include the '$' // (dollar sign) character as an alphabetic character (i.e., it can be the // first character in a name). // /////////////////////////////////////////////////////////////////////////// // Return true if the character is legal in a name bool MODEL :: BChLegal ( char ch, ECHNAME echnm, bool bInternal ) { bool bOther = bInternal && ch == ChInternal(); bool bOkForC = echnm == ECHNM_First ? __iscsymf(ch) : __iscsym(ch) || (echnm == ECHNM_Middle && ch == '.'); return bOther || bOkForC; } // Return true if the name is legal bool MODEL :: BSzLegal ( SZC szcName, bool bInternal ) { for ( int i = 0; szcName[i]; i++ ) { ECHNAME echnm = i == 0 ? ECHNM_First : (szcName[i+1] ? ECHNM_Middle : ECHNM_Last); if ( ! BChLegal( szcName[i], echnm, bInternal ) ) return false; } return true; } MODEL :: MODEL () : _pgrph(NULL), _rVersion(-1.0) { // Allocate the GRPH graph object SetPgraph(new GRPH); assert( _pgrph ); // // Define the table of known (early-defined) bit flags in this scope // #define MBN_GEN_BFLAGS_TABLE szcBitFlagNames // Include the header to generate the strings #include "mbnflags.h" // Define the table of known bit flags. for ( int i = 0; szcBitFlagNames[i]; i++ ) { // Note: this automatically interns the names into the symbol table IBFLAG ibf = Mpsymtbl().IAddBitFlag( szcBitFlagNames[i] ); } } MODEL :: ~ MODEL () { // We must clear the graph and symbol table at this point, because their // elements interreference via the names (ZSREFs) and pointers (REFPOBJs). // The symbol table is cleared first, so that no stray references to GOBJMBNs // exist when the graph object is nuked. Then the graph is cleared, so // that embedded references to strings interned in the symbol table's string // table will be removed. Mpsymtbl().clear(); // Delete the graph SetPgraph(NULL); } void MODEL :: SetPgraph ( GRPH * pgrph ) { delete _pgrph; _pgrph = pgrph; } // Add an unnamed element to the graph void MODEL :: AddElem ( GELEMLNK * pgelm ) { ASSERT_THROW( pgelm, EC_NULLP, "null ptr passed to MODEL::AddElem()" ); Pgraph()->AddElem( *pgelm ); } // Test the name for duplicate; add if not, otherwise return false bool MODEL :: BAddElem ( SZC szcName, GOBJMBN * pgobj ) { if ( ::strlen( szcName ) == 0 ) return false; // Name missing if ( Mpsymtbl().find( szcName ) ) return false; // duplicate name AddElem( szcName, pgobj ); return true; } // Add a (possibly) named object to the graph and symbol table void MODEL :: AddElem ( SZC szcName, GOBJMBN * pgelm ) { if ( szcName != NULL && ::strlen(szcName) != 0 ) { if ( Mpsymtbl().find( szcName ) ) throw GMException( EC_DUPLICATE_NAME, "attempt to add duplicate name to MBNET" ); Mpsymtbl().add( szcName, pgelm ); } AddElem( pgelm ); } void MODEL :: DeleteElem ( GOBJMBN * pgobj ) { if ( pgobj->ZsrefName().Zstr().length() > 0 ) Mpsymtbl().remove( pgobj->ZsrefName() ); else DeleteElem( (GELEMLNK *) pgobj ); } void MODEL :: DeleteElem ( GELEMLNK * pgelem ) { delete pgelem; } void MODEL :: Clone ( MODEL & model ) { ASSERT_THROW( _pgrph->ChnColl().PgelemNext() == NULL, EC_INVALID_CLONE, "cannot clone into non-empty structure" ); // Clone the descriptive information _rVersion = model._rVersion; _zsFormat = model._zsFormat; _zsCreator = model._zsCreator; _zsNetworkID = model._zsNetworkID; // Clone the symbol table _mpsymtbl.Clone( model._mpsymtbl ); // Copy the network bit flags array _vFlags = model._vFlags; // // Clone the actual contents of the network, object by object // { // Create a map to correlate old object pointers to new object pointers typedef map > MPPOBJPOBJ; MPPOBJPOBJ mppobjpobj; // Add the property types first, then all the node-like things GELEMLNK * pgelm; MODELENUM mdlenumNode( model ); for ( int icycle = 0; icycle < 2; icycle++ ) { mdlenumNode.Reset(model.Grph()); while ( pgelm = mdlenumNode.PlnkelNext() ) { // Check that it's a node (not an edge) if ( ! pgelm->BIsEType( GELEM::EGELM_NODE ) ) continue; GOBJMBN * pgobjmbn; GOBJMBN * pgobjmbnNew = NULL; DynCastThrow( pgelm, pgobjmbn ); // Clone property types on the first pass, all other nodeish things // on the second. if ( (icycle == 0) ^ (pgelm->EType() == GOBJMBN::EBNO_PROP_TYPE) ) continue; pgobjmbnNew = pgobjmbn->CloneNew( model, self ); // If the object was cloned or allowed itself to be cloned, // add it if ( pgobjmbnNew ) { assert( pgobjmbnNew->EType() == pgobjmbn->EType() ); mppobjpobj[ pgobjmbn ] = pgobjmbnNew; // Add the object as named or unnamed AddElem( pgobjmbnNew->ZsrefName(), pgobjmbnNew ); } } } // Add all the edge-like things MODELENUM mdlenumEdge( model ); while ( pgelm = mdlenumEdge.PlnkelNext() ) { // Check that it's a edge (not a node) if ( ! pgelm->BIsEType( GELEM::EGELM_EDGE ) ) continue; GEDGEMBN * pgedge; DynCastThrow( pgelm, pgedge ); GOBJMBN * pgobjmbnSource = pgedge->PobjSource(); GOBJMBN * pgobjmbnSink = pgedge->PobjSink(); assert( pgobjmbnSource && pgobjmbnSink ); GOBJMBN * pgobjmbnSourceNew = mppobjpobj[ pgobjmbnSource ]; GOBJMBN * pgobjmbnSinkNew = mppobjpobj[ pgobjmbnSink ]; assert( pgobjmbnSourceNew && pgobjmbnSinkNew ); GEDGEMBN * pgedgeNew = pgedge->CloneNew( model, self, pgobjmbnSourceNew, pgobjmbnSinkNew ); assert( pgedgeNew ); AddElem( pgedgeNew ); } } // Clone the network property list _ltProp.Clone( self, model, model._ltProp ); } GOBJMBN * MODEL :: PgobjFind ( SZC szcName ) { return Mpsymtbl().find(szcName); } void MPSYMTBL :: Clone ( const MPSYMTBL & mpsymtbl ) { // Clone all the interned strings _stszstr.Clone( mpsymtbl._stszstr ); // Clone the array of bit flag names CloneVzsref( mpsymtbl, mpsymtbl._mpzsrbit, _mpzsrbit ); // All other symbol entries must be created from above } void MPSYMTBL :: CloneVzsref ( const MPSYMTBL & mpsymtbl, const VZSREF & vzsrSource, VZSREF & vzsrTarget ) { vzsrTarget.resize( vzsrSource.size() ); for ( int i = 0; i < vzsrTarget.size(); i++ ) { SZC szc = vzsrSource[i].Szc(); vzsrTarget[i] = intern(szc); } }