utils/library.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/utils/library.cpp $
00003   version : $LastChangedRevision: 1113 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2009-12-19 14:11:32 +0100 (Sat, 19 Dec 2009) $
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., 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 

Generated on 21 Mar 2010 for frePPLe by  doxygen 1.6.1