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: 1315 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2010-07-17 18:08:53 +0200 (Sat, 17 Jul 2010) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2010 by Johan De Taeye * 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 00033 namespace frepple 00034 { 00035 namespace utils 00036 { 00037 00038 // Repository of all categories and commands 00039 DECLARE_EXPORT const MetaCategory* MetaCategory::firstCategory = NULL; 00040 DECLARE_EXPORT MetaCategory::CategoryMap MetaCategory::categoriesByTag; 00041 DECLARE_EXPORT MetaCategory::CategoryMap MetaCategory::categoriesByGroupTag; 00042 00043 // Repository of loaded modules 00044 set<string> CommandLoadLibrary::registry; 00045 00046 // Processing instruction metadata 00047 DECLARE_EXPORT const MetaCategory* Command::metadataInstruction; 00048 00049 // Number of processors. 00050 // The value initialized here is overwritten in the library initialization. 00051 DECLARE_EXPORT int Environment::processors = 1; 00052 00053 // Output logging stream, whose input buffer is shared with either 00054 // Environment::logfile or cout. 00055 DECLARE_EXPORT ostream logger(cout.rdbuf()); 00056 00057 // Output file stream 00058 DECLARE_EXPORT ofstream Environment::logfile; 00059 00060 // Name of the log file 00061 DECLARE_EXPORT string Environment::logfilename; 00062 00063 // Hash value computed only once 00064 DECLARE_EXPORT const hashtype MetaCategory::defaultHash(Keyword::hash("default")); 00065 00066 vector<PythonType*> PythonExtensionBase::table; 00067 00068 00069 DECLARE_EXPORT string Environment::searchFile(const string filename) 00070 { 00071 #ifdef _MSC_VER 00072 static char pathseperator = '\\'; 00073 #else 00074 static char pathseperator = '/'; 00075 #endif 00076 00077 // First: check the current directory 00078 struct stat stat_p; 00079 int result = stat(filename.c_str(), &stat_p); 00080 if (!result && stat_p.st_mode & S_IREAD) 00081 return filename; 00082 00083 // Second: check the FREPPLE_HOME directory, if it is defined 00084 string fullname; 00085 char * envvar = getenv("FREPPLE_HOME"); 00086 if (envvar) 00087 { 00088 fullname = envvar; 00089 if (*fullname.rbegin() != pathseperator) 00090 fullname += pathseperator; 00091 fullname += filename; 00092 result = stat(fullname.c_str(), &stat_p); 00093 if (!result && stat_p.st_mode & S_IREAD) 00094 return fullname; 00095 } 00096 00097 #ifdef DATADIRECTORY 00098 // Third: check the data directory 00099 fullname = DATADIRECTORY; 00100 if (*fullname.rbegin() != pathseperator) 00101 fullname += pathseperator; 00102 fullname.append(filename); 00103 result = stat(fullname.c_str(), &stat_p); 00104 if (!result && stat_p.st_mode & S_IREAD) 00105 return fullname; 00106 #endif 00107 00108 #ifdef LIBDIRECTORY 00109 // Fourth: check the lib directory 00110 fullname = LIBDIRECTORY; 00111 if (*fullname.rbegin() != pathseperator) 00112 fullname += pathseperator; 00113 fullname += "frepple/"; 00114 fullname += filename; 00115 result = stat(fullname.c_str(), &stat_p); 00116 if (!result && stat_p.st_mode & S_IREAD) 00117 return fullname; 00118 #endif 00119 00120 // Not found 00121 return ""; 00122 } 00123 00124 00125 DECLARE_EXPORT void Environment::setLogFile(const string& x) 00126 { 00127 // Bye bye message 00128 if (!logfilename.empty()) 00129 logger << "Stop logging at " << Date::now() << endl; 00130 00131 // Close an eventual existing log file. 00132 if (logfile.is_open()) logfile.close(); 00133 00134 // No new logfile specified: redirect to the standard output stream 00135 if (x.empty() || x == "+") 00136 { 00137 logfilename = x; 00138 logger.rdbuf(cout.rdbuf()); 00139 return; 00140 } 00141 00142 // Open the file: either as a new file, either appending to existing file 00143 if (x[0] != '+') logfile.open(x.c_str(), ios::out); 00144 else logfile.open(x.c_str()+1, ios::app); 00145 if (!logfile.good()) 00146 { 00147 // Redirect to the previous logfile (or cout if that's not possible) 00148 if (logfile.is_open()) logfile.close(); 00149 logfile.open(logfilename.c_str(), ios::app); 00150 logger.rdbuf(logfile.is_open() ? logfile.rdbuf() : cout.rdbuf()); 00151 // The log file could not be opened 00152 throw RuntimeException("Could not open log file '" + x + "'"); 00153 } 00154 00155 // Store the file name 00156 logfilename = x; 00157 00158 // Redirect the log file. 00159 logger.rdbuf(logfile.rdbuf()); 00160 00161 // Print a nice header 00162 logger << "Start logging frePPLe " << PACKAGE_VERSION << " (" 00163 << __DATE__ << ") at " << Date::now() << endl; 00164 } 00165 00166 00167 void LibraryUtils::initialize() 00168 { 00169 // Initialize only once 00170 static bool init = false; 00171 if (init) 00172 { 00173 logger << "Warning: Calling frepple::LibraryUtils::initialize() more " 00174 << "than once." << endl; 00175 return; 00176 } 00177 init = true; 00178 00179 // Set the locale to the default setting. 00180 // When not executed, the locale is the "C-locale", which restricts us to 00181 // ascii data in the input. 00182 // For Posix platforms the environment variable LC_ALL controls the locale. 00183 // Most Linux distributions these days have a default locale that supports 00184 // UTF-8 encoding, meaning that every unicode character can be 00185 // represented. 00186 // On Windows, the default is the system-default ANSI code page. The number 00187 // of characters that frePPLe supports on Windows is constrained by this... 00188 #if defined(HAVE_SETLOCALE) || defined(_MSC_VER) 00189 setlocale(LC_ALL, "" ); 00190 #endif 00191 00192 // Initialize Xerces parser 00193 xercesc::XMLPlatformUtils::Initialize(); 00194 00195 // Initialize the processing instruction metadata. 00196 Command::metadataInstruction = new MetaCategory 00197 ("instruction", ""); 00198 00199 // Register Python as a processing instruction. 00200 CommandPython::metadata2 = new MetaClass( 00201 "instruction", "python", CommandPython::processorXMLInstruction); 00202 00203 // Initialize the Python interpreter 00204 PythonInterpreter::initialize(); 00205 00206 // Query the system for the number of available processors. 00207 // The environment variable NUMBER_OF_PROCESSORS is defined automatically on 00208 // Windows platforms. On other platforms it'll have to be explicitly set 00209 // since there isn't an easy and portable way of querying this system 00210 // information. 00211 const char *c = getenv("NUMBER_OF_PROCESSORS"); 00212 if (c) 00213 { 00214 int p = atoi(c); 00215 Environment::setProcessors(p); 00216 } 00217 } 00218 00219 00220 DECLARE_EXPORT void MetaClass::registerClass (const string& a, const string& b, 00221 bool def, creatorDefault f) 00222 { 00223 // Find or create the category 00224 MetaCategory* cat 00225 = const_cast<MetaCategory*>(MetaCategory::findCategoryByTag(a.c_str())); 00226 00227 // Check for a valid category 00228 if (!cat) 00229 throw LogicException("Category " + a 00230 + " not found when registering class " + b); 00231 00232 // Update fields 00233 type = b.empty() ? "unspecified" : b; 00234 typetag = &Keyword::find(type.c_str()); 00235 category = cat; 00236 00237 // Update the metadata table 00238 cat->classes[Keyword::hash(b)] = this; 00239 00240 // Register this tag also as the default one, if requested 00241 if (def) cat->classes[Keyword::hash("default")] = this; 00242 00243 // Set method pointers to NULL 00244 factoryMethodDefault = f; 00245 } 00246 00247 00248 DECLARE_EXPORT MetaCategory::MetaCategory (const string& a, const string& gr, 00249 readController f, writeController w) 00250 { 00251 // Update registry 00252 if (!a.empty()) categoriesByTag[Keyword::hash(a)] = this; 00253 if (!gr.empty()) categoriesByGroupTag[Keyword::hash(gr)] = this; 00254 00255 // Update fields 00256 readFunction = f; 00257 writeFunction = w; 00258 type = a.empty() ? "unspecified" : a; 00259 typetag = &Keyword::find(type.c_str()); 00260 group = gr.empty() ? "unspecified" : gr; 00261 grouptag = &Keyword::find(group.c_str()); 00262 00263 // Maintain a linked list of all registered categories 00264 nextCategory = NULL; 00265 if (!firstCategory) 00266 firstCategory = this; 00267 else 00268 { 00269 const MetaCategory *i = firstCategory; 00270 while (i->nextCategory) i = i->nextCategory; 00271 const_cast<MetaCategory*>(i)->nextCategory = this; 00272 } 00273 } 00274 00275 00276 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByTag(const char* c) 00277 { 00278 // Loop through all categories 00279 CategoryMap::const_iterator i = categoriesByTag.find(Keyword::hash(c)); 00280 return (i!=categoriesByTag.end()) ? i->second : NULL; 00281 } 00282 00283 00284 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByTag(const hashtype h) 00285 { 00286 // Loop through all categories 00287 CategoryMap::const_iterator i = categoriesByTag.find(h); 00288 return (i!=categoriesByTag.end()) ? i->second : NULL; 00289 } 00290 00291 00292 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByGroupTag(const char* c) 00293 { 00294 // Loop through all categories 00295 CategoryMap::const_iterator i = categoriesByGroupTag.find(Keyword::hash(c)); 00296 return (i!=categoriesByGroupTag.end()) ? i->second : NULL; 00297 } 00298 00299 00300 DECLARE_EXPORT const MetaCategory* MetaCategory::findCategoryByGroupTag(const hashtype h) 00301 { 00302 // Loop through all categories 00303 CategoryMap::const_iterator i = categoriesByGroupTag.find(h); 00304 return (i!=categoriesByGroupTag.end()) ? i->second : NULL; 00305 } 00306 00307 00308 DECLARE_EXPORT const MetaClass* MetaCategory::findClass(const char* c) const 00309 { 00310 // Look up in the registered classes 00311 MetaCategory::ClassMap::const_iterator j = classes.find(Keyword::hash(c)); 00312 return (j == classes.end()) ? NULL : j->second; 00313 } 00314 00315 00316 DECLARE_EXPORT const MetaClass* MetaCategory::findClass(const hashtype h) const 00317 { 00318 // Look up in the registered classes 00319 MetaCategory::ClassMap::const_iterator j = classes.find(h); 00320 return (j == classes.end()) ? NULL : j->second; 00321 } 00322 00323 00324 DECLARE_EXPORT void MetaCategory::persist(XMLOutput *o) 00325 { 00326 for (const MetaCategory *i = firstCategory; i; i = i->nextCategory) 00327 if (i->writeFunction) i->writeFunction(i, o); 00328 } 00329 00330 00331 DECLARE_EXPORT const MetaClass* MetaClass::findClass(const char* c) 00332 { 00333 // Loop through all categories 00334 for (MetaCategory::CategoryMap::const_iterator i = MetaCategory::categoriesByTag.begin(); 00335 i != MetaCategory::categoriesByTag.end(); ++i) 00336 { 00337 // Look up in the registered classes 00338 MetaCategory::ClassMap::const_iterator j 00339 = i->second->classes.find(Keyword::hash(c)); 00340 if (j != i->second->classes.end()) return j->second; 00341 } 00342 // Not found... 00343 return NULL; 00344 } 00345 00346 00347 DECLARE_EXPORT void MetaClass::printClasses() 00348 { 00349 logger << "Registered classes:" << endl; 00350 // Loop through all categories 00351 for (MetaCategory::CategoryMap::const_iterator i = MetaCategory::categoriesByTag.begin(); 00352 i != MetaCategory::categoriesByTag.end(); ++i) 00353 { 00354 logger << " " << i->second->type << endl; 00355 // Loop through the classes for the category 00356 for (MetaCategory::ClassMap::const_iterator 00357 j = i->second->classes.begin(); 00358 j != i->second->classes.end(); 00359 ++j) 00360 if (j->first == Keyword::hash("default")) 00361 logger << " default ( = " << j->second->type << " )" << j->second << endl; 00362 else 00363 logger << " " << j->second->type << j->second << endl; 00364 } 00365 } 00366 00367 00368 DECLARE_EXPORT Action MetaClass::decodeAction(const char *x) 00369 { 00370 // Validate the action 00371 if (!x) throw LogicException("Invalid action NULL"); 00372 else if (!strcmp(x,"AC")) return ADD_CHANGE; 00373 else if (!strcmp(x,"A")) return ADD; 00374 else if (!strcmp(x,"C")) return CHANGE; 00375 else if (!strcmp(x,"R")) return REMOVE; 00376 else throw LogicException("Invalid action '" + string(x) + "'"); 00377 } 00378 00379 00380 DECLARE_EXPORT Action MetaClass::decodeAction(const AttributeList& atts) 00381 { 00382 // Decode the string and return the default in the absence of the attribute 00383 const DataElement* c = atts.get(Tags::tag_action); 00384 return *c ? decodeAction(c->getString().c_str()) : ADD_CHANGE; 00385 } 00386 00387 00388 DECLARE_EXPORT bool MetaClass::raiseEvent(Object* v, Signal a) const 00389 { 00390 bool result(true); 00391 for (list<Functor*>::const_iterator i = subscribers[a].begin(); 00392 i != subscribers[a].end(); ++i) 00393 // Note that we always call all subscribers, even if one or more 00394 // already replied negatively. However, an exception thrown from a 00395 // callback method will break the publishing chain. 00396 if (!(*i)->callback(v,a)) result = false; 00397 00398 // Raise the event also on the category, if there is a valid one 00399 return (category && category!=this) ? 00400 (result && category->raiseEvent(v,a)) : 00401 result; 00402 } 00403 00404 00405 Object* MetaCategory::ControllerDefault (const MetaClass* cat, const AttributeList& in) 00406 { 00407 Action act = ADD; 00408 switch (act) 00409 { 00410 case REMOVE: 00411 throw DataException 00412 ("Entity " + cat->type + " doesn't support REMOVE action"); 00413 case CHANGE: 00414 throw DataException 00415 ("Entity " + cat->type + " doesn't support CHANGE action"); 00416 default: 00417 /* Lookup for the class in the map of registered classes. */ 00418 const MetaClass* j; 00419 if (cat->category) 00420 // Class metadata passed: we already know what type to create 00421 j = cat; 00422 else 00423 { 00424 // Category metadata passed: we need to look up the type 00425 const DataElement* type = in.get(Tags::tag_type); 00426 j = static_cast<const MetaCategory&>(*cat).findClass(*type ? Keyword::hash(type->getString()) : MetaCategory::defaultHash); 00427 if (!j) 00428 { 00429 string t(*type ? type->getString() : "default"); 00430 throw LogicException("No type " + t + " registered for category " + cat->type); 00431 } 00432 } 00433 00434 // Call the factory method 00435 Object* result = j->factoryMethodDefault(); 00436 00437 // Run the callback methods 00438 if (!result->getType().raiseEvent(result, SIG_ADD)) 00439 { 00440 // Creation denied 00441 delete result; 00442 throw DataException("Can't create object"); 00443 } 00444 00445 // Creation accepted 00446 return result; 00447 } 00448 throw LogicException("Unreachable code reached"); 00449 return NULL; 00450 } 00451 00452 00453 void HasDescription::writeElement(XMLOutput *o, const Keyword &t, mode m) const 00454 { 00455 // Note that this function is never called on its own. It is always called 00456 // from the writeElement() method of a subclass. 00457 // Hence, we don't bother about the mode. 00458 o->writeElement(Tags::tag_category, cat); 00459 o->writeElement(Tags::tag_subcategory, subcat); 00460 o->writeElement(Tags::tag_description, descr); 00461 } 00462 00463 00464 void HasDescription::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement) 00465 { 00466 if (pAttr.isA(Tags::tag_category)) 00467 setCategory(pElement.getString()); 00468 else if (pAttr.isA(Tags::tag_subcategory)) 00469 setSubCategory(pElement.getString()); 00470 else if (pAttr.isA(Tags::tag_description)) 00471 setDescription(pElement.getString()); 00472 } 00473 00474 00475 DECLARE_EXPORT bool matchWildcard(const char* wild, const char *str) 00476 { 00477 // Empty arguments: always return a match 00478 if (!wild || !str) return 1; 00479 00480 const char *cp = NULL, *mp = NULL; 00481 00482 while ((*str) && *wild != '*') 00483 { 00484 if (*wild != *str && *wild != '?') 00485 // Does not match 00486 return 0; 00487 wild++; 00488 str++; 00489 } 00490 00491 while (*str) 00492 { 00493 if (*wild == '*') 00494 { 00495 if (!*++wild) return 1; 00496 mp = wild; 00497 cp = str+1; 00498 } 00499 else if (*wild == *str || *wild == '?') 00500 { 00501 wild++; 00502 str++; 00503 } 00504 else 00505 { 00506 wild = mp; 00507 str = cp++; 00508 } 00509 } 00510 00511 while (*wild == '*') wild++; 00512 return !*wild; 00513 } 00514 00515 } // end namespace 00516 } // end namespace 00517
Documentation generated for frePPLe by
