| # Gaudi Framework, version v20r2
Generated: 18 Jul 2008

- Main Page
- Namespaces
- Classes
- Files
- Directories
- Related Pages

- Search for |

- File List
- File Members

# GaudiTuples.icpp

Go to the documentation of this file.

    00001 // $Id: GaudiTuples.icpp,v 1.10 2008/05/15 08:50:32 marcocle Exp $
    00002 // ============================================================================
    00003 // CVS tag $Name: v11r1 $, version $Revision: 1.10 $
    00004 // ============================================================================
    00005 // Include files
    00006 // ============================================================================
    00007 // Gaudi
    00008 // ============================================================================
    00009 #include "GaudiKernel/IRegistry.h"
    00010 #include "GaudiKernel/ToStream.h"
    00011 // ============================================================================
    00012 // GaudiAlg
    00013 // ============================================================================
    00014 #include "GaudiAlg/GaudiTuples.h"
    00015 #include "GaudiAlg/Tuple.h"
    00016 #include "GaudiAlg/TupleObj.h"
    00017 #include "GaudiAlg/GaudiTupleAlg.h"
    00018 #include "GaudiAlg/TupleDetail.h"
    00019 #include "GaudiAlg/Print.h"
    00020 // ============================================================================
    00021 /* @file GaudiTuples.cpp
    00022  *
    00023  *  Implementation file for class : GaudiTuples
    00024  *
    00025  *  @author Chris Jones   Christopher.Rob.Jones@cern.ch
    00026  *  @author Vanya BELYAEV Ivan.Belyaev@itep.ru
    00027  *  @date   2005-08-08
    00028  */
    00029 // ============================================================================
    00030 // Disable warning on windows
    00031 #ifdef _WIN32
    00032 #pragma warning ( disable:4661 ) // incomplete explicit templates
    00033 #endif
    00034 // ============================================================================
    00035 // Destructor
    00036 //=============================================================================
    00037 template <class PBASE>
    00038 GaudiTuples<PBASE>::~GaudiTuples() {}
    00039
    00040 //=============================================================================
    00041 // Initialize ntupling
    00042 //=============================================================================
    00043 template <class PBASE>
    00044 StatusCode GaudiTuples<PBASE>::initialize()
    00045 {
    00046   // initialize base class
    00047   const StatusCode sc = PBASE::initialize();
    00048   if ( sc.isFailure() ) return sc;
    00049
    00050   if ( produceNTuples() )
    00051   {
    00052     // check the existance of service
    00053     if ( 0 == this->ntupleSvc() )
    00054     { return  this->Error( "INTupleSvc* points to NULL!" ); }
    00055     // Print ntuple path
    00056     Print( "The N-Tuple   path is set to be '" + nTuplePath() + "'",
    00057            StatusCode(StatusCode::SUCCESS, true) , MSG::DEBUG);
    00058   }
    00059   else
    00060   { this->debug() << "Production of N-Tuples is switched OFF" << endreq; }
    00061
    00062   if ( produceEvtCols() )
    00063   {
    00064     // check the existance of service
    00065     if ( 0 == this->evtColSvc() )
    00066     { return  this->Error( "INTupleSvc* points to NULL!" ); }
    00067     // Print EvtCol path
    00068     Print( "The EventCol  path is set to be '" + evtColPath() + "'",
    00069            StatusCode(StatusCode::SUCCESS, true) , MSG::DEBUG );
    00070   }
    00071   else
    00072   { this->debug() << "Production of Event Collections is switched OFF" << endreq; }
    00073
    00074   return sc;
    00075 }
    00076
    00077 //=============================================================================
    00078 // finalize ntupling
    00079 //=============================================================================
    00080 template <class PBASE>
    00081 StatusCode GaudiTuples<PBASE>::finalize()
    00082 {
    00083   if ( !( nTupleMapTitle() .empty() &&
    00084           nTupleMapNumID() .empty() &&
    00085           nTupleMapLitID() .empty() &&
    00086           evtColMapTitle() .empty() &&
    00087           evtColMapNumID() .empty() &&
    00088           evtColMapLitID() .empty()
    00089           ) )
    00090   {
    00091     const int nNtuples = nTupleMapNumID().size() + nTupleMapLitID().size();
    00092     const int nEvtCols = evtColMapNumID().size() + evtColMapLitID().size();
    00093     this->always()
    00094       << "Booked " << nNtuples << " N-Tuples and " << nEvtCols
    00095       << " Event Tag Collections" << endreq ;
    00096   }
    00097
    00098   if ( produceNTuples () && tuplesPrint  () ) { printTuples  () ; }
    00099   if ( produceEvtCols () && evtColsPrint () ) { printEvtCols () ; }
    00100
    00101   { // release ntuples and clear the container
    00102     for( TupleMapTitle::iterator itup = m_nTupleMapTitle.begin() ;
    00103          m_nTupleMapTitle.end() != itup ; ++itup )
    00104     { if( 0 != itup->second ) { itup->second->release() ; } }
    00105     m_nTupleMapTitle.clear() ;
    00106   }
    00107   { // release ntuples and clear the container
    00108     for( TupleMapNumID::iterator itup = m_nTupleMapNumID.begin() ;
    00109          m_nTupleMapNumID.end() != itup ; ++itup )
    00110     { if( 0 != itup->second ) { itup->second->release() ; } }
    00111     m_nTupleMapNumID.clear();
    00112   }
    00113   { // release ntuples and clear the container
    00114     for( TupleMapLitID::iterator itup = m_nTupleMapLitID.begin() ;
    00115          m_nTupleMapLitID.end() != itup ; ++itup )
    00116     { if( 0 != itup->second ) { itup->second->release() ; } }
    00117     m_nTupleMapLitID.clear();
    00118   }
    00119   { // release ntuples and clear the container
    00120     for( TupleMapTitle::iterator itup = m_evtColMapTitle.begin() ;
    00121          m_evtColMapTitle.end() != itup ; ++itup )
    00122     { if( 0 != itup->second ) { itup->second->release() ; } }
    00123     m_evtColMapTitle.clear();
    00124   }
    00125   { // release ntuples and clear the container
    00126     for( TupleMapNumID::iterator itup = m_evtColMapNumID.begin() ;
    00127          m_evtColMapNumID.end() != itup ; ++itup )
    00128     { if( 0 != itup->second ) { itup->second->release() ; } }
    00129     m_evtColMapNumID.clear() ;
    00130   }
    00131   { // release ntuples and clear the container
    00132     for( TupleMapLitID::iterator itup = m_evtColMapLitID.begin() ;
    00133          m_evtColMapLitID.end() != itup ; ++itup )
    00134     { if( 0 != itup->second ) { itup->second->release() ; } }
    00135     m_evtColMapLitID.clear() ;
    00136   }
    00137
    00138   // finalize base class
    00139   return PBASE::finalize();
    00140 }
    00141
    00142 // ============================================================================
    00143 // get N-tuple object ( book on-demand ) with unique identidier
    00144 // ============================================================================
    00145 template <class PBASE>
    00146 Tuples::Tuple GaudiTuples<PBASE>::nTuple
    00147 ( const std::string& title ,
    00148   const CLID&        clid  ) const
    00149 {
    00150   // look up in the table
    00151   Tuples::TupleObj* tuple = m_nTupleMapTitle[ title ]  ;
    00152   if ( 0 != tuple ) { return Tuple( tuple ) ; }                  // RETURN
    00153   // Create the tuple ID
    00154   TupleID ID;
    00155   if ( this->useNumericAutoIDs() || title.empty() )
    00156   {
    00157     if ( ! this->useNumericAutoIDs() )
    00158     {
    00159       this -> Warning( "Cannot generate automatic literal ID from an empty title ! Using numeric ID instead for nTuple ID",
    00160                        StatusCode::SUCCESS );
    00161     }
    00162     // propose the tuple ID
    00163     ID = TupleID ( m_nTupleMapNumID.size() + 1 + nTupleOffSet() );
    00164     // adjust the proposed ID
    00165     while ( nTupleExists(ID) || evtColExists(ID) ) { ++ID ; }
    00166   }
    00167   else
    00168   {
    00169     // use the title to create a unique literal ID
    00170     ID = TupleID( this->convertTitleToID(title) );
    00171     // Just in case ...
    00172     while ( nTupleExists(ID) || evtColExists(ID) ) { ID = TupleID(ID.idAsString()+"_"); }
    00173   }
    00174   // return
    00175   return nTuple( ID , title , clid ) ;
    00176 }
    00177 // ============================================================================
    00178
    00179 // ============================================================================
    00180 // Access an Event Tag Collection object (book on-demand) with unique identifier
    00181 // ============================================================================
    00182 template <class PBASE>
    00183 Tuples::Tuple GaudiTuples<PBASE>::evtCol
    00184 ( const std::string& title ,
    00185   const CLID&        clid  ) const
    00186 {
    00187   // look up in the table
    00188   Tuples::TupleObj* tuple = m_evtColMapTitle[ title ]  ;
    00189   if ( 0 != tuple ) { return Tuple( tuple ) ; }                   // RETURN
    00190   // Create the tuple ID
    00191   TupleID ID;
    00192   if ( this->useNumericAutoIDs() || title.empty() )
    00193   {
    00194     if ( ! this->useNumericAutoIDs() )
    00195     {
    00196       this -> Warning( "Cannot generate automatic literal ID from an empty title ! Using numeric ID instead for evtCol ID",
    00197                        StatusCode::SUCCESS );
    00198     }
    00199     // proposed the tuple ID
    00200     ID = m_evtColMapNumID.size() + 1 + evtColOffSet() ;
    00201     // adjust the proposed ID
    00202     while ( nTupleExists(ID) || evtColExists(ID) ) { ++ID ; }
    00203   }
    00204   else
    00205   {
    00206     // use the title to create a unique literal ID
    00207     ID = TupleID( this->convertTitleToID(title) );
    00208     // Just in case ...
    00209     while ( nTupleExists(ID) || evtColExists(ID) ) { ID = TupleID(ID.idAsString()+"_"); }
    00210   }
    00211   // return
    00212   return evtCol( ID , title , clid ) ;
    00213 }
    00214 // ============================================================================
    00215
    00216 // ============================================================================
    00217 // get N-tuple object ( book on-demand ) with forced ID
    00218 // ============================================================================
    00219 template <class PBASE>
    00220 Tuples::Tuple GaudiTuples<PBASE>::nTuple
    00221 ( const TupleID&     ID     ,
    00222   const std::string& title1 ,
    00223   const CLID&        clid   ) const
    00224 {
    00225   // Check ID
    00226   if ( ID.undefined() )
    00227   { this->Error("Undefined NTuple ID : Title='"+title1+"'"); return Tuple(0); }
    00228
    00229   // look up in the table
    00230   Tuples::TupleObj * tuple =
    00231     ( ID.numeric() ? m_nTupleMapNumID[ID.numericID()] : m_nTupleMapLitID[ID.literalID()] ) ;
    00232   if( 0 != tuple ) { return Tuple( tuple ) ; }                   // RETURN
    00233
    00234   // convert ID to the string
    00235   const std::string tID = ID.idAsString() ;
    00236
    00237   // adjust the NTuple title
    00238   const std::string title = title1.empty() ? ( "NTuple #" + tID ) : title1 ;
    00239
    00240   // book new ntuple
    00241   if( produceNTuples() )
    00242   {
    00243     // book NTupel
    00244     NTuple::Tuple * tup = 0;
    00245     if ( ID.numeric() )
    00246     {
    00247       tup = this->ntupleSvc() -> book ( nTuplePath() , ID.numericID() , clid , title );
    00248     }
    00249     else if ( ID.literal() )
    00250     {
    00251       tup = this->ntupleSvc() -> book ( nTuplePath() , ID.literalID() , clid , title );
    00252     }
    00253     else { this->Error( "Undefined NTuple ID" ); }
    00254
    00255     // assertion
    00256     this->Assert( 0 != tup , "Could not book the N-Tuple='" + title + "'" ) ;
    00257     // some printout
    00258     if( 0 != tup -> registry() && this->msgLevel(MSG::DEBUG) )
    00259     { this->debug() << "Booked NTuple '" << title << "' ID=" << tID
    00260                     << "' Path='"  << nTuplePath() << "' TS='"
    00261                     << tup -> registry() -> identifier() << "'" << endreq; }
    00262
    00263     tuple = createNTuple ( title , tup                , clid ) ;
    00264   }
    00265   else
    00266   {
    00267     tuple = createNTuple ( title , (NTuple::Tuple*) 0 , clid ) ;
    00268   }
    00269   // increment the reference
    00270   tuple -> addRef();
    00271   if   ( ID.numeric() ) { m_nTupleMapNumID [ ID.numericID() ] = tuple ; }
    00272   else                  { m_nTupleMapLitID [ ID.literalID() ] = tuple ; }
    00273   tuple -> addRef();
    00274   m_nTupleMapTitle[ title ] = tuple ;
    00275   // return
    00276   return Tuple( tuple ) ;
    00277 };
    00278
    00279 // ============================================================================
    00280 template <class PBASE>
    00281 Tuples::Tuple GaudiTuples<PBASE>::evtCol
    00282 ( const TupleID&     ID     ,
    00283   const std::string& title1 ,
    00284   const CLID&        clid   ) const
    00285 {
    00286   // Check ID
    00287   if ( ID.undefined() )
    00288   { this->Error("Undefined NTuple ID : Title='"+title1+"'"); return Tuple(0); }
    00289
    00290   // look up in the table
    00291   Tuples::TupleObj* tuple =
    00292     ( ID.numeric() ? m_evtColMapNumID[ID.numericID()] : m_evtColMapLitID[ID.literalID()] ) ;
    00293   if ( 0 != tuple ) { return Tuple( tuple ) ; }                   // RETURN
    00294
    00295   // convert ID to the string
    00296   const std::string tID = ID.idAsString() ;
    00297
    00298   // adjust the NTuple title
    00299   const std::string title = title1.empty() ? ( "EvtCol #" + tID ) : title1 ;
    00300
    00301   // book new ntuple
    00302   if( produceEvtCols() )
    00303   {
    00304     // book NTuple
    00305     NTuple::Tuple* tup = 0;
    00306     if (  ID.numeric() )
    00307     {
    00308       tup = this->evtColSvc()->book ( evtColPath() , ID.numericID() , clid , title ) ;
    00309     }
    00310     else if ( ID.literal() )
    00311     {
    00312       tup = this->evtColSvc()->book ( evtColPath() , ID.literalID() , clid , title ) ;
    00313     }
    00314     else { this->Error( "Undefined NTuple ID" ); }
    00315
    00316     // assertion
    00317     this->Assert( 0 != tup , "Could not book the EvtCol='" + title + "'" ) ;
    00318     // some printout
    00319     if( 0 != tup -> registry() && this->msgLevel(MSG::DEBUG) )
    00320     { this->debug() << "Booked EvtCol '" << title << "' ID=" << tID
    00321                     << "' Path='" << evtColPath() << "' TS='"
    00322                     << tup -> registry() -> identifier() << "'" << endreq ; }
    00323
    00324     tuple = createEvtCol ( title , tup                , clid ) ;
    00325   }
    00326   else
    00327   {
    00328     tuple = createEvtCol ( title , (NTuple::Tuple*) 0 , clid ) ;
    00329   }
    00330   // increment the reference
    00331   tuple -> addRef();
    00332   if   ( ID.numeric() ) { m_evtColMapNumID [ ID.numericID() ] = tuple ; }
    00333   else                  { m_evtColMapLitID [ ID.literalID() ] = tuple ; }
    00334   tuple -> addRef();
    00335   m_evtColMapTitle[ title ] = tuple ;
    00336   // return
    00337   return Tuple( tuple ) ;
    00338 }
    00339 // ============================================================================
    00340
    00341 // ============================================================================
    00342 // create TupleObj
    00343 // ============================================================================
    00344 template <class PBASE>
    00345 Tuples::TupleObj* GaudiTuples<PBASE>::createNTuple
    00346 ( const std::string& name  ,
    00347   NTuple::Tuple*     tuple ,
    00348   const CLID&        clid  ) const
    00349 {
    00350   return Tuples::createTupleObj
    00351     ( this , "Tuple '" + name  + "'" , tuple , clid , Tuples::NTUPLE ) ;
    00352 }
    00353 // ============================================================================
    00354
    00355 // ============================================================================
    00356 // create TupleObj for event tag collection
    00357 // ============================================================================
    00358 template <class PBASE>
    00359 Tuples::TupleObj* GaudiTuples<PBASE>::createEvtCol
    00360 ( const std::string& name  ,
    00361   NTuple::Tuple*     tuple ,
    00362   const CLID&        clid  ) const
    00363 {
    00364   return Tuples::createTupleObj
    00365     ( this , "EvtCol '" + name  + "'" , tuple , clid , Tuples::EVTCOL ) ;
    00366 }
    00367 // ============================================================================
    00368
    00369
    00370 // ============================================================================
    00371 // perform the actual printout of N-tuples
    00372 // ============================================================================
    00373 template <class PBASE>
    00374 long GaudiTuples<PBASE>::printTuples  () const
    00375 {
    00376
    00377   typedef std::set<const NTuple::Tuple*> _Tuples ;
    00378
    00379   if ( nTupleMapTitle().empty() &&
    00380        nTupleMapNumID().empty() &&
    00381        nTupleMapLitID().empty() )
    00382   { if (this->msgLevel(MSG::DEBUG)) this->debug() << "No N-Tuples   are booked" << endreq ; }
    00383   else
    00384   { this->always() << "List of booked N-Tuples in directory "
    00385                    << "\"" << nTuplePath() << "\"" << endreq ; }
    00386
    00387   _Tuples tuples ;
    00388   { // N-Tuple by Numeric ID
    00389     for ( TupleMapNumID::const_iterator entry = nTupleMapNumID().begin() ;
    00390           nTupleMapNumID().end() != entry ; ++entry )
    00391     {
    00392       if ( 0 == entry->second ) { continue ; }
    00393       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00394       if ( 0 == tuple )
    00395       { this->error() << " NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00396       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00397       tuples.insert ( tuple ) ;
    00398       this->always() << GaudiAlg::PrintTuple::print ( tuple , entry->first ) << endreq ;
    00399     }
    00400   }
    00401   { // N-Tuple by Literal ID
    00402     for ( TupleMapLitID::const_iterator entry = nTupleMapLitID().begin() ;
    00403           nTupleMapLitID().end() != entry ; ++entry )
    00404     {
    00405       if ( 0 == entry->second ) { continue ; }
    00406       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00407       if ( 0 == tuple )
    00408       { this->error() << " NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00409       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00410       tuples.insert ( tuple ) ;
    00411       this->always() << GaudiAlg::PrintTuple::print ( tuple , entry->first ) << endreq ;
    00412     }
    00413   }
    00414   { // N-Tuples by Title
    00415     for ( TupleMapTitle::const_iterator entry = nTupleMapTitle().begin() ;
    00416           nTupleMapTitle().end() != entry ; ++entry )
    00417     {
    00418       if ( 0 == entry->second ) { continue ; }
    00419       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00420       if ( 0 == tuple )
    00421       { this->error() << " NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00422       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00423       tuples.insert ( tuple ) ;
    00424       this->always() << GaudiAlg::PrintTuple::print ( tuple )             << endreq ;
    00425     }
    00426   }
    00427
    00428   return tuples.size() ;
    00429 };
    00430 // ============================================================================
    00431
    00432 // ============================================================================
    00433 // perform the actual printout of Evt Tag Collections
    00434 // ============================================================================
    00435 template <class PBASE>
    00436 long GaudiTuples<PBASE>::printEvtCols () const
    00437 {
    00438   typedef std::set<const NTuple::Tuple*> _Tuples ;
    00439
    00440   if ( evtColMapTitle().empty() &&
    00441        evtColMapNumID().empty() &&
    00442        evtColMapLitID().empty() )
    00443   { this->always() << "No Event Tag Collections are booked" << endreq ; }
    00444   else
    00445   { this->always() << "List of booked Event Tag Collections in directory "
    00446                    << "\"" << evtColPath() << "\"" << endreq ; }
    00447
    00448   _Tuples tuples ;
    00449   { // EvtCols by Numeric ID
    00450     for ( TupleMapNumID::const_iterator entry = evtColMapNumID().begin() ;
    00451           evtColMapNumID().end() != entry ; ++entry )
    00452     {
    00453       if ( 0 == entry->second ) { continue ; }
    00454       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00455       if ( 0 == tuple )
    00456       { this->error() << "NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00457       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00458       tuples.insert ( tuple ) ;
    00459       this->always() << GaudiAlg::PrintTuple::print ( tuple , entry->first )
    00460                      << " Items:"
    00461                      << Gaudi::Utils::toString ( entry->second->items() )    << endreq ;
    00462     }
    00463   }
    00464   { // EvtCols by literal ID
    00465     for ( TupleMapLitID::const_iterator entry = evtColMapLitID().begin() ;
    00466           evtColMapLitID().end() != entry ; ++entry )
    00467     {
    00468       if ( 0 == entry->second ) { continue ; }
    00469       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00470       if ( 0 == tuple )
    00471       { this->error() << "NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00472       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00473       tuples.insert ( tuple ) ;
    00474       this->always() << GaudiAlg::PrintTuple::print ( tuple , entry->first )
    00475                      << " Items:"
    00476                      << Gaudi::Utils::toString ( entry->second->items() )    << endreq ;
    00477     }
    00478   }
    00479   { // EvtCols by Title
    00480     for ( TupleMapTitle::const_iterator entry = evtColMapTitle().begin() ;
    00481           evtColMapTitle().end() != entry ; ++entry )
    00482     {
    00483       if ( 0 == entry->second ) { continue ; }
    00484       const NTuple::Tuple* tuple = entry->second->tuple() ;
    00485       if ( 0 == tuple )
    00486       { this->error() << "NTuple::Tuple* points to NULL" << endreq ; continue ; }
    00487       if ( tuples.end() != tuples.find( tuple ) ) { continue ; }
    00488       tuples.insert ( tuple ) ;
    00489       this->always() << GaudiAlg::PrintTuple::print ( tuple )
    00490                      << " Items:"
    00491                      << Gaudi::Utils::toString ( entry->second->items() ) << endreq ;
    00492     }
    00493   }
    00494
    00495   return tuples.size() ;
    00496 }
    00497
    00498 // ============================================================================
    00499 // check the existence AND validity of the N-Tuple with the given ID
    00500 // ============================================================================
    00501 template <class PBASE>
    00502 bool GaudiTuples<PBASE>::nTupleExists ( const TupleID& ID ) const
    00503 {
    00504   bool OK(false);
    00505   if ( ID.numeric() )
    00506   {
    00507     TupleMapNumID::iterator found = m_nTupleMapNumID.find( ID.numericID() ) ;
    00508     OK = ( m_nTupleMapNumID.end() != found );
    00509   }
    00510   else
    00511   {
    00512     TupleMapLitID::iterator found = m_nTupleMapLitID.find( ID.literalID() ) ;
    00513     OK = ( m_nTupleMapLitID.end() != found );
    00514   }
    00515   return OK;
    00516 }
    00517
    00518 // ============================================================================
    00519 // check the existence AND validity of the Event Tag Collection with the given ID
    00520 // ============================================================================
    00521 template <class PBASE>
    00522 bool GaudiTuples<PBASE>::evtColExists ( const TupleID& ID ) const
    00523 {
    00524   bool OK(false);
    00525   if ( ID.numeric() )
    00526   {
    00527     TupleMapNumID::iterator found = m_evtColMapNumID.find( ID.numericID() ) ;
    00528     OK = ( m_evtColMapNumID.end() != found );
    00529   }
    00530   else
    00531   {
    00532     TupleMapLitID::iterator found = m_evtColMapLitID.find( ID.literalID() ) ;
    00533     OK = ( m_evtColMapLitID.end() != found );
    00534   }
    00535   return OK;
    00536 }
    00537
    00538 // ============================================================================
    00539 // The END
    00540 // ============================================================================
    00541
    00542