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

Documentation generated for frePPLe by  doxygen