utils/library.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: http://svn.code.sf.net/p/frepple/code/trunk/src/utils/library.cpp $
00003   version : $LastChangedRevision: 1713 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-07-18 11:46:01 +0200 (Wed, 18 Jul 2012) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba                 *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Affero General Public License as published   *
00013  * by the Free Software Foundation; either version 3 of the License, or    *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
00019  * GNU Affero General Public License for more details.                     *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Affero General Public        *
00022  * License along with this program.                                        *
00023  * If not, see <http://www.gnu.org/licenses/>.                             *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 #define FREPPLE_CORE
00028 #include "frepple/utils.h"
00029 #include <sys/stat.h>
00030 
00031 // These headers are required for the loading of dynamic libraries and the
00032 // detection of the number of cores.
00033 #ifdef WIN32
00034 #include <windows.h>
00035 #else
00036 #include <dlfcn.h>
00037 #include <unistd.h>
00038 #endif
00039 
00040 
00041 namespace frepple
00042 {
00043 namespace utils
00044 {
00045 
00046 // Repository of all categories and commands
00047 DECLARE_EXPORT const MetaCategory* MetaCategory::firstCategory = NULL;
00048 DECLARE_EXPORT MetaCategory::CategoryMap MetaCategory::categoriesByTag;
00049 DECLARE_EXPORT MetaCategory::CategoryMap MetaCategory::categoriesByGroupTag;
00050 
00051 // Repository of loaded modules
00052 DECLARE_EXPORT set<string> Environment::moduleRegistry;
00053 
00054 // Number of processors.
00055 // The value initialized here is updated when the getProcessorCores function
00056 // is called the first time.
00057 DECLARE_EXPORT int Environment::processorcores = -1;
00058 
00059 // Output logging stream, whose input buffer is shared with either
00060 // Environment::logfile or cout.
00061 DECLARE_EXPORT ostream logger(cout.rdbuf());
00062 
00063 // Output file stream
00064 DECLARE_EXPORT ofstream Environment::logfile;
00065 
00066 // Name of the log file
00067 DECLARE_EXPORT string Environment::logfilename;
00068 
00069 // Hash value computed only once
00070 DECLARE_EXPORT const hashtype MetaCategory::defaultHash(Keyword::hash("default"));
00071 
00072 vector<PythonType*> PythonExtensionBase::table;
00073 
00074 
00075 void LibraryUtils::initialize(int argc, char *argv[])
00076 {
00077   // Initialize only once
00078   static bool init = false;
00079   if (init)
00080   {
00081     logger << "Warning: Calling frepple::LibraryUtils::initialize() more "
00082         << "than once." << endl;
00083     return;
00084   }
00085   init = true;
00086 
00087   // Set the locale to the default setting.
00088   // When not executed, the locale is the "C-locale", which restricts us to
00089   // ascii data in the input.
00090   // For Posix platforms the environment variable LC_ALL controls the locale.
00091   // Most Linux distributions these days have a default locale that supports
00092   // UTF-8 encoding, meaning that every unicode character can be
00093   // represented.
00094   // On Windows, the default is the system-default ANSI code page. The number
00095   // of characters that frePPLe supports on Windows is constrained by this...
00096 #if defined(HAVE_SETLOCALE) || defined(_MSC_VER)
00097   setlocale(LC_ALL, "" );
00098 #endif
00099 
00100   // Initialize Xerces parser
00101   xercesc::XMLPlatformUtils::Initialize();
00102 
00103   // Initialize the Python interpreter
00104   PythonInterpreter::initialize(argc, argv);
00105 
00106   // Register new methods in Python
00107   PythonInterpreter::registerGlobalMethod(
00108     "loadmodule", loadModule, METH_VARARGS,
00109     "Dynamically load a module in memory.");
00110 }
00111 
00112 
00113 DECLARE_EXPORT string Environment::searchFile(const string filename)
00114 {
00115 #ifdef _MSC_VER
00116   static char pathseperator = '\\';
00117 #else
00118   static char pathseperator = '/';
00119 #endif
00120 
00121   // First: check the current directory
00122   struct stat stat_p;
00123   int result = stat(filename.c_str(), &stat_p);
00124   if (!result && (stat_p.st_mode & S_IREAD))
00125     return filename;
00126 
00127   // Second: check the FREPPLE_HOME directory, if it is defined
00128   string fullname;
00129   char * envvar = getenv("FREPPLE_HOME");
00130   if (envvar)
00131   {
00132     fullname = envvar;
00133     if (*fullname.rbegin() != pathseperator)
00134       fullname += pathseperator;
00135     fullname += filename;
00136     result = stat(fullname.c_str(), &stat_p);
00137     if (!result && (stat_p.st_mode & S_IREAD))
00138       return fullname;
00139   }
00140 
00141 #ifdef DATADIRECTORY
00142   // Third: check the data directory
00143   fullname = DATADIRECTORY;
00144   if (*fullname.rbegin() != pathseperator)
00145     fullname += pathseperator;
00146   fullname.append(filename);
00147   result = stat(fullname.c_str(), &stat_p);
00148   if (!result && (stat_p.st_mode & S_IREAD))
00149     return fullname;
00150 #endif
00151 
00152 #ifdef LIBDIRECTORY
00153   // Fourth: check the lib directory
00154   fullname = LIBDIRECTORY;
00155   if (*fullname.rbegin() != pathseperator)
00156     fullname += pathseperator;
00157   fullname += "frepple/";
00158   fullname += filename;
00159   result = stat(fullname.c_str(), &stat_p);
00160   if (!result && (stat_p.st_mode & S_IREAD))
00161     return fullname;
00162 #endif
00163 
00164   // Not found
00165   return "";
00166 }
00167 
00168 
00169 DECLARE_EXPORT int Environment::getProcessorCores()
00170 {
00171   // Previously detected already
00172   if (processorcores >= 1) return processorcores;
00173 
00174   // Detect the number of cores on the machine
00175 #ifdef WIN32
00176   // Windows
00177   SYSTEM_INFO sysinfo;
00178   GetSystemInfo(&sysinfo);
00179   processorcores = sysinfo.dwNumberOfProcessors;
00180 #else
00181   // Linux, Solaris and AIX.
00182   // Tough luck for other platforms.
00183   processorcores = sysconf(_SC_NPROCESSORS_ONLN);
00184 #endif
00185   // Detection failed...
00186   if (processorcores<1) processorcores = 1;
00187   return processorcores;
00188 }
00189 
00190 
00191 DECLARE_EXPORT void Environment::setLogFile(const string& x)
00192 {
00193   // Bye bye message
00194   if (!logfilename.empty())
00195     logger << "Stop logging at " << Date::now() << endl;
00196 
00197   // Close an eventual existing log file.
00198   if (logfile.is_open()) logfile.close();
00199 
00200   // No new logfile specified: redirect to the standard output stream
00201   if (x.empty() || x == "+")
00202   {
00203     logfilename = x;
00204     logger.rdbuf(cout.rdbuf());
00205     return;
00206   }
00207 
00208   // Open the file: either as a new file, either appending to existing file
00209   if (x[0] != '+') logfile.open(x.c_str(), ios::out);
00210   else logfile.open(x.c_str()+1, ios::app);
00211   if (!logfile.good())
00212   {
00213     // Redirect to the previous logfile (or cout if that's not possible)
00214     if (logfile.is_open()) logfile.close();
00215     logfile.open(logfilename.c_str(), ios::app);
00216     logger.rdbuf(logfile.is_open() ? logfile.rdbuf() : cout.rdbuf());
00217     // The log file could not be opened
00218     throw RuntimeException("Could not open log file '" + x + "'");
00219   }
00220 
00221   // Store the file name
00222   logfilename = x;
00223 
00224   // Redirect the log file.
00225   logger.rdbuf(logfile.rdbuf());
00226 
00227   // Print a nice header
00228   logger << "Start logging frePPLe " << PACKAGE_VERSION << " ("
00229       << __DATE__ << ") at " << Date::now() << endl;
00230 }
00231 
00232 
00233 DECLARE_EXPORT void Environment::loadModule(string lib, ParameterList& parameters)
00234 {
00235   // Type definition of the initialization function
00236   typedef const char* (*func)(const ParameterList&);
00237 
00238   // Validate
00239   if (lib.empty())
00240     throw DataException("Error: No library name specified for loading");
00241 
00242 #ifdef WIN32
00243   // Load the library - The windows way
00244 
00245   // Change the error mode: we handle errors now, not the operating system
00246   UINT em = SetErrorMode(SEM_FAILCRITICALERRORS);
00247   HINSTANCE handle = LoadLibraryEx(lib.c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
00248   if (!handle) handle = LoadLibraryEx(lib.c_str(), NULL, 0);
00249   if (!handle)
00250   {
00251     // Get the error description
00252     char error[256];
00253     FormatMessage(
00254       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00255       NULL,
00256       GetLastError(),
00257       0,
00258       error,
00259       256,
00260       NULL );
00261     throw RuntimeException(error);
00262   }
00263   SetErrorMode(em);  // Restore the previous error mode
00264 
00265   // Find the initialization routine
00266   func inithandle =
00267     reinterpret_cast<func>(GetProcAddress(HMODULE(handle), "initialize"));
00268   if (!inithandle)
00269   {
00270     // Get the error description
00271     char error[256];
00272     FormatMessage(
00273       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00274       NULL,
00275       GetLastError(),
00276       0,
00277       error,
00278       256,
00279       NULL );
00280     throw RuntimeException(error);
00281   }
00282 
00283 #else
00284   // Load the library - The UNIX way
00285 
00286   // Search the frePPLe directories for the library
00287   string fullpath = Environment::searchFile(lib);
00288   if (fullpath.empty())
00289     throw RuntimeException("Module '" + lib + "' not found");
00290   dlerror(); // Clear the previous error
00291   void *handle = dlopen(fullpath.c_str(), RTLD_NOW | RTLD_GLOBAL);
00292   const char *err = dlerror();  // Pick up the error string
00293   if (err)
00294   {
00295     // Search the normal path for the library
00296     dlerror(); // Clear the previous error
00297     handle = dlopen(lib.c_str(), RTLD_NOW | RTLD_GLOBAL);
00298     err = dlerror();  // Pick up the error string
00299     if (err) throw RuntimeException(err);
00300   }
00301 
00302   // Find the initialization routine
00303   func inithandle = (func)(dlsym(handle, "initialize"));
00304   err = dlerror(); // Pick up the error string
00305   if (err) throw RuntimeException(err);
00306 #endif
00307 
00308   // Call the initialization routine with the parameter list
00309   string x = (inithandle)(parameters);
00310   if (x.empty()) throw DataException("Invalid module name returned");
00311 
00312   // Insert the new module in the registry
00313   moduleRegistry.insert(x);
00314 }
00315 
00316 
00317 DECLARE_EXPORT void MetaClass::registerClass (const string& a, const string& b,
00318     bool def, creatorDefault f)
00319 {
00320   // Find or create the category
00321   MetaCategory* cat
00322     = const_cast<MetaCategory*>(MetaCategory::findCategoryByTag(a.c_str()));
00323 
00324   // Check for a valid category
00325   if (!cat)
00326     throw LogicException("Category " + a
00327         + " not found when registering class " + b);
00328 
00329   // Update fields
00330   type = b.empty() ? "unspecified" : b;
00331   typetag = &Keyword::find(type.c_str());
00332   category = cat;
00333 
00334   // Update the metadata table
00335   cat->classes[Keyword::hash(b)] = this;
00336 
00337   // Register this tag also as the default one, if requested
00338   if (def) cat->classes[Keyword::hash("default")] = this;
00339 
00340   // Set method pointers to NULL
00341   factoryMethodDefault = f;
00342 }
00343 
00344 
00345 DECLARE_EXPORT MetaCategory::MetaCategory (const string& a, const string& gr,
00346     readController f, writeController w)
00347 {
00348   // Update registry
00349   if (!a.empty()) categoriesByTag[Keyword::hash(a)] = this;
00350   if (!gr.empty()) categoriesByGroupTag[Keyword::hash(gr)] = this;
00351 
00352   // Update fields
00353   readFunction = f;
00354   writeFunction = w;
00355   type = a.empty() ? "unspecified" : a;
00356   typetag = &Keyword::find(type.c_str());
00357   group = gr.empty() ? "unspecified" : gr;
00358   grouptag = &Keyword::find(group.c_str());
00359 
00360   // Maintain a linked list of all registered categories
00361   nextCategory = NULL;
00362   if (!firstCategory)
00363     firstCategory = this;
00364   else
00365   {
00366     const MetaCategory *i = firstCategory;
00367     while (i->nextCategory) i = i->nextCategory;
00368     const_cast<MetaCategory*>(i)->nextCategory = this;
00369   }
00370 }
00371 
00372 
00373 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByTag(const char* c)
00374 {
00375   // Loop through all categories
00376   CategoryMap::const_iterator i = categoriesByTag.find(Keyword::hash(c));
00377   return (i!=categoriesByTag.end()) ? i->second : NULL;
00378 }
00379 
00380 
00381 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByTag(const hashtype h)
00382 {
00383   // Loop through all categories
00384   CategoryMap::const_iterator i = categoriesByTag.find(h);
00385   return (i!=categoriesByTag.end()) ? i->second : NULL;
00386 }
00387 
00388 
00389 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByGroupTag(const char* c)
00390 {
00391   // Loop through all categories
00392   CategoryMap::const_iterator i = categoriesByGroupTag.find(Keyword::hash(c));
00393   return (i!=categoriesByGroupTag.end()) ? i->second : NULL;
00394 }
00395 
00396 
00397 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByGroupTag(const hashtype h)
00398 {
00399   // Loop through all categories
00400   CategoryMap::const_iterator i = categoriesByGroupTag.find(h);
00401   return (i!=categoriesByGroupTag.end()) ? i->second : NULL;
00402 }
00403 
00404 
00405 DECLARE_EXPORT const MetaClass* MetaCategory::findClass(const char* c) const
00406 {
00407   // Look up in the registered classes
00408   MetaCategory::ClassMap::const_iterator j = classes.find(Keyword::hash(c));
00409   return (j == classes.end()) ? NULL : j->second;
00410 }
00411 
00412 
00413 DECLARE_EXPORT const MetaClass* MetaCategory::findClass(const hashtype h) const
00414 {
00415   // Look up in the registered classes
00416   MetaCategory::ClassMap::const_iterator j = classes.find(h);
00417   return (j == classes.end()) ? NULL : j->second;
00418 }
00419 
00420 
00421 DECLARE_EXPORT void MetaCategory::persist(XMLOutput *o)
00422 {
00423   for (const MetaCategory *i = firstCategory; i; i = i->nextCategory)
00424     if (i->writeFunction) i->writeFunction(i, o);
00425 }
00426 
00427 
00428 DECLARE_EXPORT const MetaClass* MetaClass::findClass(const char* c)
00429 {
00430   // Loop through all categories
00431   for (MetaCategory::CategoryMap::const_iterator i = MetaCategory::categoriesByTag.begin();
00432       i != MetaCategory::categoriesByTag.end(); ++i)
00433   {
00434     // Look up in the registered classes
00435     MetaCategory::ClassMap::const_iterator j
00436       = i->second->classes.find(Keyword::hash(c));
00437     if (j != i->second->classes.end()) return j->second;
00438   }
00439   // Not found...
00440   return NULL;
00441 }
00442 
00443 
00444 DECLARE_EXPORT void MetaClass::printClasses()
00445 {
00446   logger << "Registered classes:" << endl;
00447   // Loop through all categories
00448   for (MetaCategory::CategoryMap::const_iterator i = MetaCategory::categoriesByTag.begin();
00449       i != MetaCategory::categoriesByTag.end(); ++i)
00450   {
00451     logger << "  " << i->second->type << endl;
00452     // Loop through the classes for the category
00453     for (MetaCategory::ClassMap::const_iterator
00454         j = i->second->classes.begin();
00455         j != i->second->classes.end();
00456         ++j)
00457       if (j->first == Keyword::hash("default"))
00458         logger << "    default ( = " << j->second->type << " )" << j->second << endl;
00459       else
00460         logger << "    " << j->second->type << j->second << endl;
00461   }
00462 }
00463 
00464 
00465 DECLARE_EXPORT Action MetaClass::decodeAction(const char *x)
00466 {
00467   // Validate the action
00468   if (!x) throw LogicException("Invalid action NULL");
00469   else if (!strcmp(x,"AC")) return ADD_CHANGE;
00470   else if (!strcmp(x,"A")) return ADD;
00471   else if (!strcmp(x,"C")) return CHANGE;
00472   else if (!strcmp(x,"R")) return REMOVE;
00473   else throw LogicException("Invalid action '" + string(x) + "'");
00474 }
00475 
00476 
00477 DECLARE_EXPORT Action MetaClass::decodeAction(const AttributeList& atts)
00478 {
00479   // Decode the string and return the default in the absence of the attribute
00480   const DataElement* c = atts.get(Tags::tag_action);
00481   return *c ? decodeAction(c->getString().c_str()) : ADD_CHANGE;
00482 }
00483 
00484 
00485 DECLARE_EXPORT bool MetaClass::raiseEvent(Object* v, Signal a) const
00486 {
00487   bool result(true);
00488   for (list<Functor*>::const_iterator i = subscribers[a].begin();
00489       i != subscribers[a].end(); ++i)
00490     // Note that we always call all subscribers, even if one or more
00491     // already replied negatively. However, an exception thrown from a
00492     // callback method will break the publishing chain.
00493     if (!(*i)->callback(v,a)) result = false;
00494 
00495   // Raise the event also on the category, if there is a valid one
00496   return (category && category!=this) ?
00497       (result && category->raiseEvent(v,a)) :
00498       result;
00499 }
00500 
00501 
00502 Object* MetaCategory::ControllerDefault (const MetaClass* cat, const AttributeList& in)
00503 {
00504   Action act = ADD;
00505   switch (act)
00506   {
00507     case REMOVE:
00508       throw DataException
00509       ("Entity " + cat->type + " doesn't support REMOVE action");
00510     case CHANGE:
00511       throw DataException
00512       ("Entity " + cat->type + " doesn't support CHANGE action");
00513     default:
00514       /* Lookup for the class in the map of registered classes. */
00515       const MetaClass* j;
00516       if (cat->category)
00517         // Class metadata passed: we already know what type to create
00518         j = cat;
00519       else
00520       {
00521         // Category metadata passed: we need to look up the type
00522         const DataElement* type = in.get(Tags::tag_type);
00523         j = static_cast<const MetaCategory&>(*cat).findClass(*type ? Keyword::hash(type->getString()) : MetaCategory::defaultHash);
00524         if (!j)
00525         {
00526           string t(*type ? type->getString() : "default");
00527           throw LogicException("No type " + t + " registered for category " + cat->type);
00528         }
00529       }
00530 
00531       // Call the factory method
00532       Object* result = j->factoryMethodDefault();
00533 
00534       // Run the callback methods
00535       if (!result->getType().raiseEvent(result, SIG_ADD))
00536       {
00537         // Creation denied
00538         delete result;
00539         throw DataException("Can't create object");
00540       }
00541 
00542       // Creation accepted
00543       return result;
00544   }
00545   throw LogicException("Unreachable code reached");
00546   return NULL;
00547 }
00548 
00549 
00550 void HasDescription::writeElement(XMLOutput *o, const Keyword &t, mode m) const
00551 {
00552   // Note that this function is never called on its own. It is always called
00553   // from the writeElement() method of a subclass.
00554   // Hence, we don't bother about the mode.
00555   o->writeElement(Tags::tag_category, cat);
00556   o->writeElement(Tags::tag_subcategory, subcat);
00557   o->writeElement(Tags::tag_description, descr);
00558 }
00559 
00560 
00561 void HasDescription::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00562 {
00563   if (pAttr.isA(Tags::tag_category))
00564     setCategory(pElement.getString());
00565   else if (pAttr.isA(Tags::tag_subcategory))
00566     setSubCategory(pElement.getString());
00567   else if (pAttr.isA(Tags::tag_description))
00568     setDescription(pElement.getString());
00569 }
00570 
00571 
00572 DECLARE_EXPORT bool matchWildcard(const char* wild, const char *str)
00573 {
00574   // Empty arguments: always return a match
00575   if (!wild || !str) return 1;
00576 
00577   const char *cp = NULL, *mp = NULL;
00578 
00579   while ((*str) && *wild != '*')
00580   {
00581     if (*wild != *str && *wild != '?')
00582       // Does not match
00583       return 0;
00584     wild++;
00585     str++;
00586   }
00587 
00588   while (*str)
00589   {
00590     if (*wild == '*')
00591     {
00592       if (!*++wild) return 1;
00593       mp = wild;
00594       cp = str+1;
00595     }
00596     else if (*wild == *str || *wild == '?')
00597     {
00598       wild++;
00599       str++;
00600     }
00601     else
00602     {
00603       wild = mp;
00604       str = cp++;
00605     }
00606   }
00607 
00608   while (*wild == '*') wild++;
00609   return !*wild;
00610 }
00611 
00612 } // end namespace
00613 } // end namespace
00614 

Documentation generated for frePPLe by  doxygen