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