Gaudi Framework, version v20r3

Generated: 24 Nov 2008

configGenerator Class Reference

Collaboration diagram for configGenerator:

Collaboration graph
[legend]

List of all members.


Detailed Description

Definition at line 70 of file genconf.cpp.


Public Member Functions

 configGenerator (const string &pkgName, const string &outputDirName)
int genConfig (const Strings_t &modules)
 main entry point of this class:
  • iterate over all the modules (ie: library names)
  • for each module extract component informations
  • eventually generate the header/body/trailer python file and "Db" file

void setConfigurableModule (const std::string &moduleName)
 customize the Module name where configurable base classes are defined
void setConfigurableDefaultName (const std::string &defaultName)
 customize the default name for configurable instances
void setConfigurableAlgorithm (const std::string &cfgAlgorithm)
 customize the configurable base class for Algorithm component
void setConfigurableAlgTool (const std::string &cfgAlgTool)
 customize the configurable base class for AlgTool component
void setConfigurableAuditor (const std::string &cfgAuditor)
 customize the configurable base class for AlgTool component
void setConfigurableService (const std::string &cfgService)
 customize the configurable base class for Service component

Private Member Functions

void genComponent (const std::string &libName, const std::string &componentName, const std::string &componentType, const vector< Property * > &properties)
void genImport (std::ostream &s, const boost::format &frmt, std::string indent)
void genHeader (std::ostream &pyOut, std::ostream &dbOut)
void genBody (std::ostream &pyOut, std::ostream &dbOut)
void genTrailer (std::ostream &pyOut, std::ostream &dbOut)
void pythonizeValue (const Property *prop, string &pvalue, string &ptype)
 handle the "marshalling" of Properties
void pythonizeName (string &name)
 Translates a valid C++ typename into a valid python one.

Private Attributes

string m_pkgName
 name of the package we are processing
string m_outputDirName
 absolute path to the directory where genconf will store auto-generated files (Configurables and ConfigurableDb)
stringstream m_pyBuf
 buffer of auto-generated configurables
bool m_importGaudiHandles
 switch to decide if the generated configurables need to import GaudiHandles (ie: if one of the components has a XyzHandle<T>)
stringstream m_dbBuf
 buffer of generated configurables informations for the "Db" file The "Db" file is holding informations about the generated configurables This file is later one used by the PropertyProxy.py to locate Configurables and know their default values, host module,.
GaudiUtils::HashMap
< std::string, std::string > 
m_configurable
 Configurable customization.

Constructor & Destructor Documentation

configGenerator::configGenerator ( const string &  pkgName,
const string &  outputDirName 
) [inline]

Definition at line 100 of file genconf.cpp.

00101                                                  : 
00102     m_pkgName           ( pkgName ),
00103     m_outputDirName     ( outputDirName ),
00104     m_pyBuf             ( ),
00105     m_importGaudiHandles( false ),
00106     m_dbBuf             ( ),
00107     m_configurable      ( )
00108   {}


Member Function Documentation

int configGenerator::genConfig ( const Strings_t modules  ) 

main entry point of this class:

  • iterate over all the modules (ie: library names)
  • for each module extract component informations
  • eventually generate the header/body/trailer python file and "Db" file

not enough information... skip it

no Properties, so don't bother create Configurables...

: no Configurable for this component. yet... continue;

write-out files for this library

Definition at line 408 of file genconf.cpp.

00410 {
00411   //--- Disable checking StatusCode -------------------------------------------
00412   StatusCode::disableChecking();
00413   
00414   const Strings_t::const_iterator endLib = libs.end();
00415   
00416   const std::string gaudiSvc = "GaudiSvc";
00417   const bool isGaudiSvc = ( std::find( libs.begin(), endLib, gaudiSvc ) != endLib );
00418   
00419   //--- Instantiate ApplicationMgr --------------------------------------------
00420   if ( !isGaudiSvc && createAppMgr() ) {
00421     cout << "ERROR: ApplicationMgr can not be created. Check environment" << endl;
00422     return EXIT_FAILURE;
00423   }
00424   
00425   //--- Iterate over component factories --------------------------------------
00426   Scope factories = Scope::ByName(PLUGINSVC_FACTORY_NS);
00427   if ( !factories ) {
00428     cout << "ERROR: No PluginSvc factory namespace could be found" << endl;
00429     return EXIT_FAILURE;
00430   }
00431   
00432   ISvcLocator* svcLoc = Gaudi::svcLocator();
00433   IService*    dummySvc = new Service( "DummySvc", svcLoc );
00434   dummySvc->addRef();
00435 
00436   bool allGood = true;
00437 
00438   // iterate over all the requested libraries
00439   for ( Strings_t::const_iterator iLib=libs.begin(); iLib != endLib; ++iLib ) {
00440     
00441     std::cout << ":::: processing library: " << *iLib << "..." << std::endl;
00442     
00443     // reset state
00444     m_importGaudiHandles = false;
00445     m_pyBuf.str("");
00446     m_dbBuf.str("");
00447 
00448     // Scan the pluginSvc namespace and store the "background" of already
00449     // alived components, so we can extract our signal later on
00450     set<string> bkgNames;
00451     if ( !isGaudiSvc ) {
00452       for ( Member_Iterator it = factories.FunctionMember_Begin(); 
00453             it != factories.FunctionMember_End(); ++it ) {
00454         string ident = getId(*it);
00455         if ( PluginService::Debug() > 0 ) {
00456           cout << "::: " << ident << endl;
00457         }
00458         bkgNames.insert( ident );
00459       }
00460     }
00461     const set<string>::const_iterator bkgNamesEnd = bkgNames.end();
00462     
00463     //--- Load component library ----------------------------------------------
00464     System::ImageHandle handle;
00465     unsigned long err = System::loadDynamicLib( *iLib, &handle );
00466     if ( err != 1 ) {
00467       cout << "ERROR: " << System::getLastErrorString() << endl;
00468       allGood = false;
00469       continue;
00470     }
00471     
00472     for ( Member_Iterator it = factories.FunctionMember_Begin(); 
00473           it != factories.FunctionMember_End(); 
00474           ++it ) {
00475       const string ident = getId(*it);
00476       if ( bkgNames.find(ident) != bkgNamesEnd ) {
00477         if ( PluginService::Debug() > 0 ) {
00478           cout << "\t==> skipping [" << ident << "]..." << endl;
00479         }
00480         continue;
00481       }
00482       
00483       // Atlas contributed code (patch #1247)
00484       // Skip the generation of configurables if the component does not come
00485       // from the same library we are processing (i.e. we found a symbol that
00486       // is coming from a library loaded by the linker).
00487       // Windows implementation is empty.
00488       if ( !DsoUtils::inDso( *it, DsoUtils::libNativeName(*iLib) ) ) {
00489         cout << "WARNING: library [" << *iLib << "] requested factory "
00490         << "from another library ["
00491         << DsoUtils::dsoName(*it) << "]"
00492         << " ==> [" << ident << "] !!"
00493         << endl;
00494         continue;
00495       }
00496 
00497       const string rtype = it->TypeOf().ReturnType().Name();
00498       string type;
00499       bool known = true;
00500       if      ( ident == "ApplicationMgr" ) type = "ApplicationMgr";
00501       else if ( rtype == "IInterface*" )    type = "IInterface";
00502       else if ( rtype == "IAlgorithm*" )    type = "Algorithm";
00503       else if ( rtype == "IService*" )      type = "Service";
00504       else if ( rtype == "IAlgTool*" )      type = "AlgTool";
00505       else if ( rtype == "IAuditor*" )      type = "Auditor";
00506       else if ( rtype == "IConverter*" )    type = "Converter";
00507       else if ( rtype == "DataObject*" )    type = "DataObject";
00508       else                                  type = "Unknown", known = false;
00509       string name = ident;
00510       // handle possible problems with templated components
00511       boost::trim(name);
00512 
00513       cout << " - component: " << name << endl;
00514       
00515       if ( type == "IInterface" ) {
00518         continue;
00519       }
00520       
00521       if ( type == "Converter" || type == "DataObject" ) {
00523         continue;
00524       }
00525       
00526       //if ( type == "ApplicationMgr" ) {
00529       //}
00530 
00531       if ( !known ) {
00532         cout << "WARNING: Unknown (return) type [" << rtype << "] !!\n"
00533              << "WARNING: component [" << ident << "] is skipped !"
00534              << endl;
00535         allGood = false;
00536         continue;
00537       }
00538       
00539       string cname = "DefaultName";
00540       Object dummy;
00541       vector<void*>  args; 
00542       args.reserve( 3 );
00543       if ( type == "AlgTool" ) {
00544         args.resize( 3 );
00545         args[0] = &cname;
00546         args[1] = &type;
00547         args[2] = dummySvc;
00548       }
00549       else {
00550         args.resize( 2 );
00551         args[0] = &cname;
00552         args[1] = svcLoc;
00553       }
00554       IProperty* prop = 0;
00555       try {
00556         if ( type == "Algorithm" ) {
00557           IAlgorithm* obj = static_cast<IAlgorithm*>(it->Invoke(dummy,args).Address());
00558           prop = dynamic_cast<IProperty*>(obj);
00559         }
00560         else if ( type == "Service") {
00561           IService* obj = static_cast<IService*>(it->Invoke(dummy,args).Address());
00562           prop = dynamic_cast<IProperty*>(obj);
00563         }
00564         else if ( type == "AlgTool") {
00565           IAlgTool* obj = static_cast<IAlgTool*>(it->Invoke(dummy,args).Address());
00566           prop = dynamic_cast<IProperty*>(obj);
00567         }
00568         else if ( type == "Auditor") {
00569           IAuditor* obj = static_cast<IAuditor*>(it->Invoke(dummy,args).Address());
00570           prop = dynamic_cast<IProperty*>(obj);
00571         }
00572         else if ( type == "ApplicationMgr") {
00573           //svcLoc->queryInterface(IProperty::interfaceID(), pp_cast<void>(&prop));
00574           svcLoc->queryInterface(IProperty::interfaceID(), (void**)(&prop));
00575         }
00576         else {
00577           IInterface* obj = static_cast<IInterface*>(it->Invoke(dummy,args).Address());
00578           prop = dynamic_cast<IProperty*>(obj);
00579         }
00580       }
00581       catch ( exception& e ) {
00582         cout << "ERROR: Error instantiating " << name 
00583              << " from " << *iLib << endl;
00584         cout << "ERROR: Got exception: " << e.what() << endl;
00585         allGood = false;
00586         continue;
00587       }
00588       catch ( ... ) {
00589         cout << "ERROR: Error instantiating " << name 
00590              << " from " << *iLib << endl;
00591         allGood = false;
00592         continue;
00593       }
00594       if( prop ) {
00595         genComponent( *iLib, name, type, prop->getProperties() );
00596         prop->release();
00597       } else {
00598         cout << "ERROR: could not cast IInterface* object to an IProperty* !\n"
00599              << "ERROR: return type from PluginSvc is [" << rtype << "]...\n"
00600              << "ERROR: NO Configurable will be generated for [" 
00601              << name << "] !"
00602              << endl;
00603         allGood = false;
00604       }
00605     } //> end loop over factories
00606 
00610     const std::string pyName = ( fs::path(m_outputDirName) /
00611                   fs::path(*iLib+"Conf.py") ).string();
00612     const std::string dbName = ( fs::path(m_outputDirName) /
00613                   fs::path(*iLib+"_confDb.py") ).string();
00614 
00615     std::fstream py( pyName.c_str(),
00616               std::ios_base::out|std::ios_base::trunc );
00617     std::fstream db( dbName.c_str(),
00618               std::ios_base::out|std::ios_base::trunc );
00619     
00620     genHeader ( py, db );
00621     genBody   ( py, db );
00622     genTrailer( py, db );
00623     
00624   } //> end loop over libraries
00625   
00626   dummySvc->release();
00627   dummySvc = 0;
00628   
00629   return allGood ? EXIT_SUCCESS : EXIT_FAILURE;
00630 }

void configGenerator::setConfigurableModule ( const std::string &  moduleName  )  [inline]

customize the Module name where configurable base classes are defined

Definition at line 117 of file genconf.cpp.

00118   {
00119     m_configurable[ "Module" ] = moduleName;
00120   }

void configGenerator::setConfigurableDefaultName ( const std::string &  defaultName  )  [inline]

customize the default name for configurable instances

Definition at line 123 of file genconf.cpp.

00124   {
00125     m_configurable[ "DefaultName" ] = defaultName;
00126   }

void configGenerator::setConfigurableAlgorithm ( const std::string &  cfgAlgorithm  )  [inline]

customize the configurable base class for Algorithm component

Definition at line 129 of file genconf.cpp.

00130   {
00131     m_configurable[ "Algorithm" ] = cfgAlgorithm;
00132   }

void configGenerator::setConfigurableAlgTool ( const std::string &  cfgAlgTool  )  [inline]

customize the configurable base class for AlgTool component

Definition at line 135 of file genconf.cpp.

00136   {
00137     m_configurable[ "AlgTool" ] = cfgAlgTool;
00138   }

void configGenerator::setConfigurableAuditor ( const std::string &  cfgAuditor  )  [inline]

customize the configurable base class for AlgTool component

Definition at line 141 of file genconf.cpp.

00142   {
00143     m_configurable[ "Auditor" ] = cfgAuditor;
00144   }

void configGenerator::setConfigurableService ( const std::string &  cfgService  )  [inline]

customize the configurable base class for Service component

Definition at line 147 of file genconf.cpp.

00148   {
00149     m_configurable[ "Service" ] = cfgService;
00150     m_configurable[ "ApplicationMgr" ] = cfgService;
00151   }

void configGenerator::genComponent ( const std::string &  libName,
const std::string &  componentName,
const std::string &  componentType,
const vector< Property * > &  properties 
) [private]

Definition at line 722 of file genconf.cpp.

00727 {
00728   string cname = componentName;
00729   pythonizeName(cname);
00730     
00731   typedef GaudiUtils::HashMap<std::string, std::string> PropertyDoc_t;
00732   PropertyDoc_t propDoc;
00733   
00734   m_pyBuf << "\n";
00735   m_pyBuf << "class " << cname 
00736           << "( " << m_configurable[componentType] << " ) :" 
00737           << "\n";
00738   m_pyBuf << "  __slots__ = { \n";
00739   for ( vector<Property*>::const_iterator it = properties.begin() ; 
00740         it != properties.end(); ++it ) {
00741     const string pname  = (*it)->name();
00742     string pvalue, ptype;
00743     pythonizeValue( (*it), pvalue, ptype );
00744     m_pyBuf << "    '" << pname << "' : " << pvalue <<", # " << ptype << "\n";
00745     
00746     if ( (*it)->documentation() != "none" ) {
00747       propDoc[pname] = (*it)->documentation();
00748     }
00749     
00750   }
00751   m_pyBuf << "  }\n";
00752   m_pyBuf << "  _propertyDocDct = { \n";
00753   for ( PropertyDoc_t::const_iterator iProp = propDoc.begin();
00754         iProp != propDoc.end();
00755         ++iProp ) {
00756     m_pyBuf << std::setw(5)  
00757             << "'"       << iProp->first  << "' : "
00758             << "\"\"\" "  << iProp->second << " \"\"\",\n";
00759   }
00760   m_pyBuf << "  }\n";
00761   
00762   m_pyBuf 
00763     << "  def __init__(self, name = " << m_configurable["DefaultName"]
00764                                       << ", **kwargs):\n"
00765     << "      super(" << cname << ", self).__init__(name)\n"
00766     << "      for n,v in kwargs.items():\n"
00767     << "         setattr(self, n, v)\n"
00768     << "  def getDlls( self ):\n"
00769     << "      return '" << libName << "'\n"
00770     << "  def getType( self ):\n"
00771     << "      return '" << componentName  << "'\n"
00772     << "  pass # class " << cname << "\n"
00773     << flush;
00774   
00775   // name of the auto-generated module
00776   const string pyName = ( fs::path(m_outputDirName) /
00777                           fs::path(libName+"Conf.py") ).string();
00778   const string modName = fs::basename( fs::path( pyName ).leaf() );
00779   
00780   // now the db part
00781   m_dbBuf 
00782     << py_tab << "cfgDb.add( configurable = '" << cname << "',\n"
00783     << py_tab << "           package = '" << m_pkgName << "',\n"
00784     << py_tab << "           module  = '" << m_pkgName << "." << modName << "',\n"
00785     << py_tab << "           lib     = '" << libName << "' )\n"
00786     << flush;
00787 
00788 }

void configGenerator::genImport ( std::ostream &  s,
const boost::format &  frmt,
std::string  indent = "" 
) [private]

Definition at line 632 of file genconf.cpp.

00634                                                        {
00635   
00636   std::string::size_type pos = 0, nxtpos = 0;
00637   std::string mod;
00638   
00639   while ( std::string::npos != pos ){
00640     // find end of module name
00641     nxtpos = m_configurable["Module"].find_first_of(',',pos);
00642     
00643     // Prepare import string
00644     mod = m_configurable["Module"].substr(pos,nxtpos-pos);
00645     std::ostringstream import;
00646     import << boost::format(frmt) % mod;
00647     
00648     // append a normal import or a try/excpet enclosed one depending
00649     // on availability of a fall-back bodule (next in the list)
00650     if ( std::string::npos == nxtpos ) {
00651       // last possible module
00652       s << indent << import.str() << "\n" << flush;
00653       pos = std::string::npos;
00654     } else {
00655       // we have a fallback for this
00656       s << indent << "try:\n"
00657         << indent << py_tab << import.str() << "\n"
00658         << indent << "except ImportError:\n"
00659         << flush;
00660       pos = nxtpos+1;
00661     }
00662     // increase indentation level for next iteration
00663     indent += py_tab;
00664   }
00665 }

void configGenerator::genHeader ( std::ostream &  pyOut,
std::ostream &  dbOut 
) [private]

Definition at line 668 of file genconf.cpp.

00671 {
00672   // python file part
00673   time_t rawtime;
00674   time( &rawtime );
00675   py << "#" << ctime(&rawtime) //<< "\n"
00676      << "\"\"\"Automatically generated. DO NOT EDIT please\"\"\"\n";
00677   if ( m_importGaudiHandles ) {
00678     py << "from GaudiKernel.GaudiHandles import *\n";
00679   }
00680   
00681   genImport(py,boost::format("from %1%.Configurable import *"));
00682   
00683   // db file part
00684   db << "##  -*- python -*-  \n"
00685      << "# db file automatically generated by genconf on: " 
00686      << ctime(&rawtime); // << "\n";
00687   db << "## insulates outside world against anything bad that could happen\n"
00688      << "## also prevents global scope pollution\n"
00689      << "def _fillCfgDb():\n";
00690   genImport(db,boost::format("from %1%.ConfigurableDb import CfgDb"),py_tab);
00691   
00692   db << "\n"
00693      << py_tab << "# get a handle on the repository of Configurables\n"
00694      << py_tab << "cfgDb = CfgDb()\n"
00695      << "\n"
00696      << py_tab << "# populate the repository with informations on Configurables \n"
00697      << "\n"
00698      << flush;
00699 }

void configGenerator::genBody ( std::ostream &  pyOut,
std::ostream &  dbOut 
) [inline, private]

Definition at line 160 of file genconf.cpp.

00161                                     { 
00162     pyOut << m_pyBuf.str() << flush;
00163     dbOut << m_dbBuf.str() << flush;
00164   }

void configGenerator::genTrailer ( std::ostream &  pyOut,
std::ostream &  dbOut 
) [private]

Definition at line 701 of file genconf.cpp.

00704 {
00705   // db file part
00706   db << py_tab << "return #_fillCfgDb\n"
00707      << "# fill cfgDb at module import...\n"
00708      << "try:\n"
00709      << py_tab << "_fillCfgDb()\n"
00710      << py_tab << "#house cleaning...\n"
00711      << py_tab << "del _fillCfgDb\n"
00712      << "except Exception,err:\n"
00713      << py_tab << "print \"Py:ConfigurableDb   ERROR Problem with [%s] content!"
00714                << "\" % __name__\n"
00715      << py_tab << "print \"Py:ConfigurableDb   ERROR\",err\n"
00716      << py_tab << "print \"Py:ConfigurableDb   ERROR   ==> culprit is package ["
00717                << m_pkgName << "] !\"\n"
00718      << std::flush;
00719 }

void configGenerator::pythonizeValue ( const Property prop,
string &  pvalue,
string &  ptype 
) [private]

handle the "marshalling" of Properties

Definition at line 801 of file genconf.cpp.

00804 {
00805   const std::string cvalue = p->toString();
00806   const type_info& ti = *p->type_info();
00807   if ( ti == typeid(bool) ) {
00808     pvalue = ( cvalue == "0" || cvalue == "False" || cvalue == "false" )
00809            ? "False" 
00810            : "True";
00811     ptype  = "bool";
00812   }
00813   else if ( ti == typeid(char)  || ti == typeid(signed char)    
00814             || ti == typeid(unsigned char)  ||
00815             ti == typeid(short) || ti == typeid(unsigned short) ||
00816             ti == typeid(int)   || ti == typeid(unsigned int)   ||
00817             ti == typeid(long)  || ti == typeid(unsigned long) ) {
00818     pvalue = cvalue;
00819     ptype  = "int";
00820   }
00821   else if ( ti == typeid(long long) || ti == typeid(unsigned long long) ) {
00822     pvalue = cvalue + "L";
00823     ptype  = "long";
00824   }
00825   else if ( ti == typeid(float) || ti == typeid(double) ) {
00826     // forces python to handle this as a float: put a dot in there...
00827     pvalue = boost::to_lower_copy(cvalue);
00828     if ( pvalue == "nan" ) {
00829       pvalue = "float('nan')";
00830       std::cout << "WARNING: default value for ["
00831                 << p->name() << "] is NaN !!"
00832                 << std::endl;
00833     } else if ( std::string::npos == pvalue.find(".") &&
00834                 std::string::npos == pvalue.find("e")  ) {
00835       pvalue = cvalue + ".0";
00836     }
00837     ptype  = "float";
00838   }
00839   else if ( ti == typeid(string) ) {
00840     
00841     pvalue = "'"+cvalue+"'";
00842     ptype  = "str";
00843   }
00844   else if ( ti == typeid(GaudiHandleBase) ) {
00845     m_importGaudiHandles = true;
00846     const GaudiHandleProperty& hdl 
00847       = dynamic_cast<const GaudiHandleProperty&>(*p);
00848     const GaudiHandleBase&     base = hdl.value();
00849     
00850     pvalue = base.pythonRepr();
00851     ptype  = "GaudiHandle";
00852   }
00853   else if ( ti == typeid(GaudiHandleArrayBase) ) {
00854     m_importGaudiHandles = true;
00855     const GaudiHandleArrayProperty& hdl 
00856       = dynamic_cast<const GaudiHandleArrayProperty&>(*p);
00857     const GaudiHandleArrayBase&     base = hdl.value();
00858     
00859     pvalue = base.pythonRepr();
00860     ptype  = "GaudiHandleArray";
00861   }
00862   else {
00863     pvalue = cvalue;
00864     ptype  = "list";
00865   }
00866 }

void configGenerator::pythonizeName ( string &  name  )  [private]

Translates a valid C++ typename into a valid python one.

Definition at line 790 of file genconf.cpp.

00792 {
00793   static string  in("<>&*,: ().");
00794   static string out("__rp__s___");
00795   for ( string::iterator i = name.begin(); i != name.end(); ++i ) {
00796     if ( in.find(*i) != string::npos ) *i = out[in.find(*i)];
00797   }  
00798 }


Member Data Documentation

string configGenerator::m_pkgName [private]

name of the package we are processing

Definition at line 73 of file genconf.cpp.

absolute path to the directory where genconf will store auto-generated files (Configurables and ConfigurableDb)

Definition at line 77 of file genconf.cpp.

stringstream configGenerator::m_pyBuf [private]

buffer of auto-generated configurables

Definition at line 80 of file genconf.cpp.

switch to decide if the generated configurables need to import GaudiHandles (ie: if one of the components has a XyzHandle<T>)

Definition at line 84 of file genconf.cpp.

stringstream configGenerator::m_dbBuf [private]

buffer of generated configurables informations for the "Db" file The "Db" file is holding informations about the generated configurables This file is later one used by the PropertyProxy.py to locate Configurables and know their default values, host module,.

..

Definition at line 90 of file genconf.cpp.

GaudiUtils::HashMap<std::string, std::string> configGenerator::m_configurable [private]

Configurable customization.

Contains customization for:

  • Name of the module where configurable base classes are defined
  • Name of the configurable base class for the Algorithm component
  • Name of the configurable base class for the AlgTool component
  • Name of the configurable base class for the Service component

Definition at line 97 of file genconf.cpp.


The documentation for this class was generated from the following file:

Generated at Mon Nov 24 14:45:01 2008 for Gaudi Framework, version v20r3 by Doxygen version 1.5.6 written by Dimitri van Heesch, © 1997-2004