utils/library.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/utils/library.cpp $ 00003 version : $LastChangedRevision: 1479 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-07-10 14:57:01 +0200 (Sun, 10 Jul 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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() 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(); 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