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  doxygen