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