diff --git a/include/cantera/base/ctexceptions.h b/include/cantera/base/ctexceptions.h index 0b0b5f5c0..e41f6274e 100644 --- a/include/cantera/base/ctexceptions.h +++ b/include/cantera/base/ctexceptions.h @@ -12,9 +12,6 @@ #include #include -// See file misc.cpp for implementations of methods/functions declared -// here. - namespace Cantera { @@ -75,8 +72,7 @@ class CanteraError : public std::exception public: //! Normal Constructor for the CanteraError base class /*! - * This class doesn't have any storage associated with it. In its - * constructor, a call to the Application class is made to store + * In the constructor, a call to the Application class is made to store * the strings associated with the generated error condition. * * @param procedure String name for the function within which the error was @@ -86,14 +82,15 @@ public: CanteraError(std::string procedure, std::string msg); //! Destructor for base class does nothing - virtual ~CanteraError() throw(); + virtual ~CanteraError() throw() {}; + protected: //! Empty base constructor is made protected so that it may be used only by //! inherited classes. /*! * We want to discourage throwing an error containing no information. */ - CanteraError(); + CanteraError() {}; }; //! Array size error. diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index 4a91690c4..5e2cb1101 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -405,9 +405,6 @@ void close_XML_File(std::string file); * A, B, and C to be written (in nested HTML lists), loglevel = * 2 results in messages only being written from A and B, etc. */ -//@{ -//@} - //!Create a new group for log messages. /*! @@ -508,7 +505,6 @@ inline void endLogGroup(std::string title="") {} inline void write_logfile(std::string file = "log.html") {} #endif - //! This routine will locate an XML node in either the input //! XML tree or in another input file specified by the file //! part of the file_ID string. diff --git a/src/base/application.cpp b/src/base/application.cpp new file mode 100644 index 000000000..e0ae6155d --- /dev/null +++ b/src/base/application.cpp @@ -0,0 +1,810 @@ +#include "application.h" + +#include "cantera/base/ctexceptions.h" +#include "cantera/base/ctml.h" +#include "cantera/base/stringUtils.h" +#include "cantera/base/xml.h" + +#include +#include +#include + +using std::string; +using std::endl; + +#ifdef _WIN32 +#include +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "advapi32") +#endif + +// If running multiple threads in a cpp application, the Application class +// is the only internal object that is single instance with static data. +// Synchronize access to those data structures Using macros to avoid +// polluting code with a lot of ifdef's + +#ifdef THREAD_SAFE_CANTERA + +#include +#include +//! Mutex for input directory access +static boost::mutex dir_mutex; + +//! Mutex for access to string messages +static boost::mutex msg_mutex; + +//! Mutex for creating singeltons within the application object +static boost::mutex app_mutex; + +// Mutex for controlling access to the log file +//static boost::mutex log_mutex; + +//! Mutex for controlling access to XML file storage +static boost::mutex xml_mutex; + +//! Macro for locking input directory access +#define DIR_LOCK() boost::mutex::scoped_lock d_lock(dir_mutex) + +//! Macro for locking access to string messages +#define MSG_LOCK() boost::mutex::scoped_lock m_lock(msg_mutex) + +//! Macro for locking creating singletons in the application state +#define APP_LOCK() boost::mutex::scoped_lock a_lock(app_mutex) + +//! Macro for locking XML file writing +#define XML_LOCK() boost::mutex::scoped_lock x_lock(xml_mutex) + +#ifdef WITH_HTML_LOGS +//static boost::mutex html_mutex; // html logs +//#define HTML_LOCK() boost::mutex::scoped_lock h_lock(html_mutex) +#endif + +#if defined(BOOST_HAS_WINTHREADS) +#include +typedef unsigned int cthreadId_t ; +class thread_equal +{ +public: + bool operator()(cthreadId_t L, cthreadId_t R) { + return L == R ; + } +} ; +cthreadId_t getThisThreadId() +{ + return ::GetCurrentThreadId() ; +} +#elif defined(BOOST_HAS_PTHREADS) + +typedef pthread_t cthreadId_t ; +class thread_equal +{ +public: + bool operator()(cthreadId_t L, cthreadId_t R) { + return pthread_equal(L, R) ; + } +} ; +cthreadId_t getThisThreadId() +{ + return pthread_self() ; +} +#elif defined(BOOST_HAS_MPTASKS) +typedef MPTaskID cthreadId_t ; +class thread_equal +{ +public: + bool operator()(cthreadId_t L, cthreadId_t R) { + return L == R ; + } +} ; +cthreadId_t getThisThreadId() +{ + return MPCurrentTaskID() ; +} +#endif + +#else +#define DIR_LOCK() +#define MSG_LOCK() +#define APP_LOCK() +//#define LOG_LOCK() +#define XML_LOCK() + +#ifdef WITH_HTML_LOGS +//#define HTML_LOCK() +#endif + +#endif + +namespace Cantera { + +Application::Messages::Messages() : + errorMessage(0), + errorRoutine(0), + logwriter(0) +#ifdef WITH_HTML_LOGS + ,xmllog(0), + current(0), + loglevel(0), + loglevels(0), + loggroups(0) +#endif +{ + // install a default logwriter that writes to standard + // output / standard error + logwriter = new Logger(); +} + +Application::Messages::Messages(const Messages& r) : + errorMessage(r.errorMessage), + errorRoutine(r.errorRoutine), + logwriter(0) +#ifdef WITH_HTML_LOGS + , xmllog(r.xmllog), + current(r.current), + loglevel(r.loglevel), + loglevels(r.loglevels), + loggroups(r.loggroups) +#endif +{ + // install a default logwriter that writes to standard + // output / standard error + logwriter = new Logger(*(r.logwriter)); +} + +Application::Messages& Application::Messages::operator=(const Messages& r) { + if (this == &r) { + return *this; + } + errorMessage = r.errorMessage; + errorRoutine = r.errorRoutine; + logwriter = new Logger(*(r.logwriter)); +#ifdef WITH_HTML_LOGS + xmllog = r.xmllog; + current = r.current; + loglevel = r.loglevel; + loglevels = r.loglevels; + loggroups = r.loggroups; +#endif + return *this; +} + +Application::Messages::~Messages() { + delete logwriter; +#ifdef WITH_HTML_LOGS + if (xmllog) { + write_logfile("orphan"); + } +#endif +} + +// Set an error condition in the application class without throwing an exception +void Application::Messages::addError(std::string r, std::string msg) +{ + errorMessage.push_back(msg); + errorRoutine.push_back(r); +} + +// Return the number of errors encountered so far +int Application::Messages::getErrorCount() +{ + return static_cast(errorMessage.size()) ; +} + +void Application::Messages::setLogger(Logger* _logwriter) +{ + if (logwriter == _logwriter) { + return ; + } + if (logwriter != 0) { + delete logwriter; + logwriter = 0 ; + } + logwriter = _logwriter; +} + +// Return an integer specifying the application environment. +int Application::Messages::getUserEnv() +{ + return logwriter->env() ; +} + +// Write an error message and terminate execution +void Application::Messages::logerror(const std::string& msg) +{ + logwriter->error(msg) ; +} + +#ifdef WITH_HTML_LOGS + +// Write a message to the screen +void Application::Messages::writelog(const char* pszmsg) +{ + logwriter->write(pszmsg) ; +} + +// Write a message to the screen +void Application::Messages::writelog(const std::string& msg) +{ + logwriter->write(msg); +} + +// Write an endl to the screen and flush output +void Application::Messages::writelogendl() +{ + logwriter->writeendl(); +} + +void Application::Messages::beginLogGroup(std::string title, int _loglevel /*=-99*/) +{ + // loglevel is a member of the Messages class. + if (_loglevel != -99) { + loglevel = _loglevel; + } else { + loglevel--; + } + if (loglevel <= 0) { + return; + } + // Add the current loglevel to the vector of loglevels + loglevels.push_back(loglevel); + // Add the title of the current logLevel to the vector of titles + loggroups.push_back(title); + // If we haven't started an XML tree for the log file, do so here + if (xmllog == 0) { + // The top of this tree will have a zero pointer. + xmllog = new XML_Node("html"); + current = &xmllog->addChild("ul"); + } + // Add two children to the XML tree. + current = ¤t->addChild("li",""+title+""); + current = ¤t->addChild("ul"); +} + +void Application::Messages::addLogEntry(std::string tag, std::string value) +{ + if (loglevel > 0 && current) { + current->addChild("li",tag+": "+value); + } +} + +void Application::Messages::addLogEntry(std::string tag, doublereal value) +{ + if (loglevel > 0 && current) { + current->addChild("li",tag+": "+fp2str(value)); + } +} + +void Application::Messages::addLogEntry(std::string tag, int value) +{ + if (loglevel > 0 && current) { + current->addChild("li",tag+": "+int2str(value)); + } +} + +void Application::Messages::addLogEntry(std::string msg) +{ + if (loglevel > 0 && current) { + current->addChild("li",msg); + } +} + +void Application::Messages::endLogGroup(std::string title) +{ + if (loglevel <= 0) { + return; + } + AssertThrowMsg(current, "Application::Messages::endLogGroup", + "Error while ending a LogGroup. This is probably due to an unmatched" + " beginnning and ending group"); + current = current->parent(); + AssertThrowMsg(current, "Application::Messages::endLogGroup", + "Error while ending a LogGroup. This is probably due to an unmatched" + " beginnning and ending group"); + current = current->parent(); + // Get the loglevel of the previous level and get rid of + // vector entry in loglevels. + loglevel = loglevels.back(); + loglevels.pop_back(); + if (title != "" && title != loggroups.back()) { + writelog("Logfile error." + "\n beginLogGroup: "+ loggroups.back()+ + "\n endLogGroup: "+title+"\n"); + write_logfile("logerror"); + } else if (loggroups.size() == 1) { + write_logfile(loggroups.back()+"_log"); + loggroups.clear(); + loglevels.clear(); + } else { + loggroups.pop_back(); + } +} + +void Application::Messages::write_logfile(std::string file) +{ + if (!xmllog) { + return; + } + std::string::size_type idot = file.rfind('.'); + std::string ext = ""; + std::string nm = file; + if (idot != std::string::npos) { + ext = file.substr(idot, file.size()); + nm = file.substr(0,idot); + } else { + ext = ".html"; + nm = file; + } + + // see if file exists. If it does, find an integer that + // can be appended to the name to create the name of a file + // that does not exist. + std::string fname = nm + ext; + std::ifstream f(fname.c_str()); + if (f) { + int n = 0; + while (1 > 0) { + n++; + fname = nm + int2str(n) + ext; + std::ifstream f(fname.c_str()); + if (!f) { + break; + } + } + } + + // Now we have a file name that does not correspond to any + // existing file. Open it as an output stream, and dump the + // XML (HTML) tree to it. + + if (xmllog) { + std::ofstream f(fname.c_str()); + // go to the top of the tree, and write it all. + xmllog->root().write(f); + f.close(); + writelog("Log file " + fname + " written.\n"); + delete xmllog; + xmllog = 0; + current = 0; + } +} + +#endif // WITH_HTML_LOGS + +Application::Application() : + inputDirs(0), + stop_on_error(false), + options(), + tmp_dir("."), + xmlfiles(), + m_sleep("1"), + pMessenger() { +#if !defined( THREAD_SAFE_CANTERA ) + pMessenger = std::auto_ptr(new Messages()); +#endif + // if TMP or TEMP is set, use it for the temporary + // directory + char* ctmpdir = getenv("CANTERA_TMPDIR"); + if (ctmpdir != 0) { + tmp_dir = std::string(ctmpdir); + } else { + char* tmpdir = getenv("TMP"); + if (tmpdir == 0) { + tmpdir = getenv("TEMP"); + } + if (tmpdir != 0) { + tmp_dir = std::string(tmpdir); + } + } + + // if SLEEP is set, use it as the sleep time + char* sleepstr = getenv("SLEEP"); + if (sleepstr != 0) { + m_sleep = std::string(sleepstr); + } + + // install a default logwriter that writes to standard + // output / standard error + // logwriter = new Logger(); + //#ifdef WITH_HTML_LOGS + // // HTML log files + // xmllog = 0; + // current = 0; + // loglevel = 0; + //#endif + setDefaultDirectories(); +#if defined(THREAD_SAFE_CANTERA) + Unit::units() ; +#endif +} + +Application* Application::Instance() { + APP_LOCK(); + if (Application::s_app == 0) { + Application::s_app = new Application(); + } + return s_app; +} + +Application::~Application() +{ + std::map::iterator pos; + for (pos = xmlfiles.begin(); pos != xmlfiles.end(); ++pos) { + pos->second->unlock(); + delete pos->second; + pos->second = 0; + } +} + +void Application::ApplicationDestroy() { + APP_LOCK(); + if (Application::s_app != 0) { + delete Application::s_app; + Application::s_app = 0; + } +} + +void Application::thread_complete() +{ +#if defined(THREAD_SAFE_CANTERA) + pMessenger.removeThreadMessages() ; +#endif +} + + +XML_Node* Application::get_XML_File(std::string file, int debug) +{ + XML_LOCK(); + std::string path = ""; + /* + try { + path = findInputFile(file); + } + catch (CanteraError) { + string::size_type idot = file.rfind('.'); + string ext = ""; + if (idot != string::npos) { + ext = file.substr(idot, file.size()); + string ctifile = file.substr(0,idot)+".cti"; + try { + path = findInputFile(ctifile); + } + catch (CanteraError) { + path = findInputFile(file); + } + } + else + path = findInputFile(file); + } + */ + // The code above will try to process a cti file if the xml + // file is not found. But I (dgg) don't think it makes much sense, + // so it is replaced by: + path = findInputFile(file); +#ifdef _WIN32 + // RFB: For Windows make the path POSIX compliant so code looking for directory + // separators is simpler. Just look for '/' not both '/' and '\\' + std::replace_if(path.begin(), path.end(), + std::bind2nd(std::equal_to(), '\\'), '/') ; +#endif + + string ff = path; + if (xmlfiles.find(path) + == xmlfiles.end()) { + /* + * Check whether or not the file is XML. If not, it will + * be first processed with the preprocessor. We determine + * whether it is an XML file by looking at the file extension. + */ + string::size_type idot = path.rfind('.'); + string ext; + if (idot != string::npos) { + ext = path.substr(idot, path.size()); + } else { + ext = ""; + idot = path.size(); + } + if (ext != ".xml" && ext != ".ctml") { + /* + * We will assume that we are trying to open a cti file. + * First, determine the name of the xml file, ff, derived from + * the cti file. + * In all cases, we will write the xml file to the current + * directory. + */ + string::size_type islash = path.rfind('/'); + if (islash != string::npos) { + ff = string("./")+path.substr(islash+1,idot-islash - 1) + ".xml"; + } else { + ff = string("./")+path.substr(0,idot) + ".xml"; + } +#ifdef DEBUG_PATHS + cout << "get_XML_File(): Expected location of xml file = " + << ff << endl; +#endif + /* + * Do a search of the existing XML trees to determine if we have + * already processed this file. If we have, return a pointer to + * the processed xml tree. + */ + if (xmlfiles.find(ff) != xmlfiles.end()) { +#ifdef DEBUG_PATHS + cout << "get_XML_File(): File, " << ff << ", was previously read." + << " Retrieving the stored xml tree." << endl; +#endif + return xmlfiles[ff]; + } + /* + * Ok, we didn't find the processed XML tree. Do the conversion + * to xml, possibly overwriting the file, ff, in the process. + */ + ctml::ct2ctml(path.c_str(),debug); + } else { + ff = path; + } + /* + * Take the XML file ff, open it, and process it, creating an + * XML tree, and then adding an entry in the map. We will store + * the absolute pathname as the key for this map. + */ + std::ifstream s(ff.c_str()); + + XML_Node* x = new XML_Node("doc"); + if (s) { + x->build(s); + x->lock(); + xmlfiles[ff] = x; + } else { + string estring = "cannot open "+ff+" for reading."; + estring += "Note, this error indicates a possible configuration problem."; + throw CanteraError("get_XML_File", estring); + } + } + + /* + * Return the XML node pointer. At this point, we are sure that the + * lookup operation in the return statement will return a valid + * pointer. + */ + return xmlfiles[ff]; +} + + +void Application::close_XML_File(std::string file) +{ + XML_LOCK(); + if (file == "all") { + std::map::iterator + b = xmlfiles.begin(), + e = xmlfiles.end(); + for (; b != e; ++b) { + b->second->unlock(); + delete b->second; + xmlfiles.erase(b->first); + } + } else if (xmlfiles.find(file) != xmlfiles.end()) { + xmlfiles[file]->unlock(); + delete xmlfiles[file]; + xmlfiles.erase(file); + } +} + +#ifdef _WIN32 +long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName, + std::string& value, const std::string& defaultValue) +{ + + HKEY key; + long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key); + if (open_error != ERROR_SUCCESS) { + return open_error; + } + value = defaultValue; + CHAR buffer[1024]; + DWORD bufferSize = sizeof(buffer); + ULONG error; + error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize); + if (ERROR_SUCCESS == error) { + value = buffer; + } + RegCloseKey(key); + return error; +} +#endif + +void Application::setTmpDir(std::string tmp) +{ + APP_LOCK(); + tmp_dir = tmp ; +} + +std::string Application::getTmpDir() +{ + APP_LOCK(); + return tmp_dir ; +} + +std::string Application::sleep() +{ + APP_LOCK(); + return m_sleep ; +} + +void Application::Messages::popError() +{ + if (static_cast(errorMessage.size()) > 0) { + errorRoutine.pop_back() ; + errorMessage.pop_back() ; + } +} + +std::string Application::Messages::lastErrorMessage() +{ + if (static_cast(errorMessage.size()) > 0) { + string head = + "\n\n************************************************\n" + " Cantera Error! \n" + "************************************************\n\n"; + return head+string("\nProcedure: ")+errorRoutine.back() + +string("\nError: ")+errorMessage.back(); + } else { + return ""; + } +} + +void Application::Messages::getErrors(std::ostream& f) +{ + int i = static_cast(errorMessage.size()); + if (i == 0) { + return; + } + f << endl << endl; + f << "************************************************" << endl; + f << " Cantera Error! " << endl; + f << "************************************************" << endl + << endl; + int j; + for (j = 0; j < i; j++) { + f << endl; + f << "Procedure: " << errorRoutine[j] << endl; + f << "Error: " << errorMessage[j] << endl; + } + f << endl << endl; + errorMessage.clear(); + errorRoutine.clear(); +} + +void Application::Messages::logErrors() +{ + int i = static_cast(errorMessage.size()); + if (i == 0) { + return; + } + writelog("\n\n"); + writelog("************************************************\n"); + writelog(" Cantera Error! \n"); + writelog("************************************************\n\n"); + int j; + for (j = 0; j < i; j++) { + writelog("\n"); + writelog(string("Procedure: ")+ errorRoutine[j]+" \n"); + writelog(string("Error: ")+ errorMessage[j]+" \n"); + } + writelog("\n\n"); + errorMessage.clear(); + errorRoutine.clear(); +} + + + +void Application::setDefaultDirectories() +{ + std::vector& dirs = inputDirs; + + // always look in the local directory first + dirs.push_back("."); + +#ifdef _WIN32 + // Under Windows, the Cantera setup utility records the installation + // directory in the registry. Data files are stored in the 'data' and + // 'templates' subdirectories of the main installation directory. + + std::string installDir; + readStringRegistryKey("SOFTWARE\\Cantera\\Cantera 2.0", "InstallDir", installDir, ""); + if (installDir != "") { + dirs.push_back(installDir + "data"); + dirs.push_back(installDir + "templates"); + } +#endif + +#ifdef DARWIN + // + // add a default data location for Mac OS X + // + dirs.push_back("/Applications/Cantera/data"); +#endif + + // + // if environment variable CANTERA_DATA is defined, then add + // it to the search path + // + if (getenv("CANTERA_DATA") != 0) { + string datadir = string(getenv("CANTERA_DATA")); + dirs.push_back(datadir); + } + + // CANTERA_DATA is defined in file config.h. This file is written + // during the build process (unix), and points to the directory + // specified by the 'prefix' option to 'configure', or else to + // /usr/local/cantera. +#ifdef CANTERA_DATA + string datadir = string(CANTERA_DATA); + dirs.push_back(datadir); +#endif +} + +void Application::addDataDirectory(std::string dir) +{ + DIR_LOCK() ; + if (inputDirs.size() == 0) { + setDefaultDirectories(); + } + string d = stripnonprint(dir); + size_t m, n = inputDirs.size(); + + // don't add if already present + for (m = 0; m < n; m++) { + if (d == inputDirs[m]) { + return; + } + } + + inputDirs.push_back(d); +} + +std::string Application::findInputFile(std::string name) +{ + DIR_LOCK() ; + string::size_type islash = name.find('/'); + string::size_type ibslash = name.find('\\'); + string inname; + std::vector& dirs = inputDirs; + + int nd; + if (islash == string::npos && ibslash == string::npos) { + nd = static_cast(dirs.size()); + int i; + inname = ""; + for (i = 0; i < nd; i++) { + inname = dirs[i] + "/" + name; + std::ifstream fin(inname.c_str()); + if (fin) { + fin.close(); + return inname; + } + } + string msg; + msg = "\nInput file " + name + + " not found in director"; + msg += (nd == 1 ? "y " : "ies "); + for (i = 0; i < nd; i++) { + msg += "\n'" + dirs[i] + "'"; + if (i < nd-1) { + msg += ", "; + } + } + msg += "\n\n"; + msg += "To fix this problem, either:\n"; + msg += " a) move the missing files into the local directory;\n"; + msg += " b) define environment variable CANTERA_DATA to\n"; + msg += " point to the directory containing the file."; + throw CanteraError("findInputFile", msg); + } + + return name; +} + +Application* Application::s_app = 0; + +} // namespace Cantera diff --git a/src/base/application.h b/src/base/application.h new file mode 100644 index 000000000..f560cb2e1 --- /dev/null +++ b/src/base/application.h @@ -0,0 +1,787 @@ +#ifndef CT_BASE_APPLICATION_H +#define CT_BASE_APPLICATION_H + +#include "cantera/base/config.h" +#include "cantera/base/logger.h" + +#include +#include +#include +#include + +namespace Cantera { + +class XML_Node; + +/*! + * @defgroup globalData Global Data + * + * Global data are available anywhere. There are two kinds. + * Cantera has an assortment of constant values for physical parameters. + * Also, Cantera maintains a collection of global data which is specific + * to each process that invokes Cantera functions. This process-specific + * data is stored in the class Application. + */ + + +//! Class to hold global data. +/*! + * Class Application is the top-level + * class that stores data that should persist for the duration of + * the process. The class should not be instantiated directly; + * instead, it is instantiated as needed by the functions declared + * here. At most one instance is created, and it is not destroyed + * until the process terminates. + * + * @ingroup HTML_logs + * @ingroup textlogs + * @ingroup globalData + */ +class Application +{ +protected: + //! Class to carry out messages + /*! + * @ingroup HTML_logs + */ + class Messages + { + public: + //! Constructor for the Messages class + /*! Constructor for the Messages class which is a subclass + * of the Application class. + */ + Messages(); + + //! Copy Constructor for the Messages class + /*! Constructor for the Messages class which is a subclass + * of the Application class. + * @param r Message to be copied + */ + Messages(const Messages& r); + + //! Assignment operator + /*! + * @param r Message to be copied + */ + Messages& operator=(const Messages& r); + + //! Destructor for the Messages class + ~Messages(); + + //! Set an error condition in the application class without + //! throwing an exception. + /*! + * This routine adds an error message to the end of the stack + * of errors that Cantera accumulates in the Application + * class. + * @param r location + * @param msg Description of the error + * @ingroup errorhandling + */ + void addError(std::string r, std::string msg); + + //! Return the number of errors that have been encountered so far. + /*! + * @ingroup errorhandling + */ + int getErrorCount() ; + + //! Discard the last error message + /*! + * %Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine eliminates + * the last exception to be added to that stack. + * + * @ingroup errorhandling + */ + void popError() ; + + //! Retrieve the last error message in a string + /*! + * This routine will retrieve the last error message and return + * it in the return string. + * + * @ingroup errorhandling + */ + std::string lastErrorMessage() ; + + //! Prints all of the error messages to an ostream + /*! + * Print all of the error messages using function writelog. + * Write out all of the saved error messages to the ostream f + * Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine writes + * out all of the error messages to the ostream + * and then clears them from internal storage. + * + * @param f ostream which will receive the error messages + * + * @ingroup errorhandling + */ + void getErrors(std::ostream& f) ; + + //! Prints all of the error messages using writelog + /*! + * Print all of the error messages using function writelog. + * Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine writes + * out all of the error messages + * and then clears them from internal storage. + * + * @ingroup errorhandling + */ + void logErrors(); + + //! Write a message to the screen. + /*! + * The string may be of any + * length, and may contain end-of-line characters. This method is + * used throughout %Cantera to write log messages. + * + * @param msg c++ string to be written to the screen + * @ingroup textlogs + */ + void writelog(const std::string& msg); + + //! Write an end of line and flush output + void writelogendl(); + + //! Write a message to the screen. + /*! + * The string may be of any + * length, and may contain end-of-line characters. This method is + * used throughout %Cantera to write log messages. + * + * @param pszmsg c character string to be written to the screen + * @ingroup textlogs + */ + void writelog(const char* pszmsg) ; + + //! Write an error message and quit. + /*! + * The default behavior is + * to write to the standard error stream, and then call + * exit(). Note that no end-of-line character is appended to + * the message, and so if one is desired it must be included + * in the string. Note that this default behavior will + * terminate the application Cantera is invoked from (MATLAB, + * Excel, etc.) If this is not desired, then derive a class + * and reimplement this method. + * + * @param msg Error message to be written to cerr. + */ + void logerror(const std::string& msg) ; + + //! Returns an integer specifying the application environment + int getUserEnv() ; + + //! Install a logger. + /*! + * Called by the language interfaces to install an appropriate logger. + * The logger is used for the writelog() function + * + * @param logwriter Pointer to a logger object + * @see Logger. + * @ingroup textlogs + */ + void setLogger(Logger* logwriter) ; + +#ifdef WITH_HTML_LOGS + + //!Create a new group for log messages. + /*! + * Usually this is called + * upon entering the function, with the title parameter equal to + * the name of the function or method. Subsequent messages + * written with addLogEntry will appear grouped under this + * heading, until endLogGroup() is called. + * + * @param title String name of the LogGroup + * @param loglevel loglevel of the group. + * @ingroup HTML_logs + */ + void beginLogGroup(std::string title, int loglevel) ; + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, std::string value) ; + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, doublereal value) ; + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, int value) ; + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "msg". + * + * @param msg Message to be added + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string msg) ; + + //! Close the current group of log messages. + /*! + * This is typically + * called just before leaving a function or method, to close the + * group of messages that were output from this + * function. Subsequent messages written with addLogEntry() will + * appear at the next-higher level in the outline, unless + * beginLogGroup() is called first to create a new group. + * + * @param title Name of the log group. It defaults to the most recent + * log group created. + */ + void endLogGroup(std::string title) ; + + //! Write the HTML log file. + /*! + * Log entries are stored in memory in + * an XML tree until this function is called, which writes the + * tree to a file and clears the entries stored in memory. The + * output file will have the name specified in the 'file' + * argument. If this argument has no extension, the extension + * '.html' will be appended. Also, if the file already exists, an + * integer will be appended to the name so that no existing log + * file will be overwritten. + * WITH_HTML_LOGS must be defined. + * + * @param file Name of the file to be written + */ + void write_logfile(std::string file); +#endif + + protected: + //! Current list of error messages + std::vector errorMessage; + + //! Current error Routine + std::vector errorRoutine; + + //! Current pointer to the logwriter + Logger* logwriter; +#ifdef WITH_HTML_LOGS + //! Current pointer to the top of the XML_Node tree for the current HTML log + XML_Node* xmllog; + + //! Pointer to the last current position in the XML_Node tree for the current HTML log + XML_Node* current; + + //! Current value of the loglevel + int loglevel; + + //! Vector of loglevels for loggroups that are open + std::vector loglevels; + + //! Current vector of loggroups that are open + std::vector loggroups; +#endif + } ; + +#ifdef THREAD_SAFE_CANTERA + //! Typedef for thread specific messages + typedef boost::shared_ptr< Messages > pMessages_t ; + + //! Typedef for map between a thread and the message + typedef std::map< cthreadId_t, pMessages_t > threadMsgMap_t ; + + //! Class that stores thread messages for each thread, and retrieves them + //! based on the thread id. + class ThreadMessages + { + public: + //! Constructor + ThreadMessages() { + } + + //! Provide a pointer deferencing overloaded operator + /*! + * @return returns a pointer to Messages + */ + Messages* operator->() { + MSG_LOCK() ; + cthreadId_t curId = getThisThreadId() ; + threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ; + if (iter != m_threadMsgMap.end()) { + return (iter->second.get()) ; + } + pMessages_t pMsgs(new Messages()) ; + m_threadMsgMap.insert(std::pair< cthreadId_t, pMessages_t >(curId, pMsgs)) ; + return pMsgs.get() ; + } + + //! Remove a local thread message + void removeThreadMessages() { + MSG_LOCK() ; + cthreadId_t curId = getThisThreadId() ; + threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ; + if (iter != m_threadMsgMap.end()) { + m_threadMsgMap.erase(iter) ; + } + } + + private: + //! Thread Msg Map + threadMsgMap_t m_threadMsgMap ; + } ; +#endif + + +protected: + //! Constructor for class sets up the initial conditions + //! Protected ctor access thru static member function Instance + Application(); + +public: + //! Return a pointer to the one and only instance of class Application + /*! + * If the an Application object has not yet been created it is created + */ + static Application* Instance(); + + //! Destructor for class deletes global data + /*! + * Delete any open XML trees, the logwriter, and + * the XML log, if any. + */ + virtual ~Application(); + + //! Static function that destroys the application class's data + static void ApplicationDestroy(); + + //! Set the directory where temporary files may be written. + /*! + * @param tmp Name of the directory + */ + void setTmpDir(std::string tmp) ; + + //! get the name of a directory where temporary files may be written. + std::string getTmpDir() ; + + //! Pause the application for a certain amount + /*! + * This has been needed in the past for some network file systems. + */ + std::string sleep() ; + + //! Set an error condition in the application class without + //! throwing an exception. + /*! + * This routine adds an error message to the end of the stack + * of errors that Cantera accumulates in the Application + * class. + * @param r location + * @param msg Description of the error + * @ingroup errorhandling + */ + void addError(std::string r, std::string msg) { + pMessenger->addError(r, msg) ; + } + + //! Return the number of errors that have been encountered so far. + /*! + * @ingroup error handling + */ + int getErrorCount() { + return pMessenger->getErrorCount() ; + } + + //! Discard the last error message + /*! + * %Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine eliminates + * the last exception to be added to that stack. + * + * @ingroup errorhandling + */ + void popError() { + pMessenger->popError() ; + } + + //! Retrieve the last error message in a string + /*! + * This routine will retrieve the last error message and return + * it in the return string. + * + * @ingroup errorhandling + */ + std::string lastErrorMessage() { + return pMessenger->lastErrorMessage() ; + } + + //! Prints all of the error messages to an ostream + /*! + * Print all of the error messages using function writelog. + * Write out all of the saved error messages to the ostream f + * Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine writes + * out all of the error messages to the ostream + * and then clears them from internal storage. + * + * @param f ostream which will receive the error messages + * + * @ingroup errorhandling + */ + void getErrors(std::ostream& f) { + pMessenger->getErrors(f) ; + } + + //! Prints all of the error messages using writelog + /*! + * Print all of the error messages using function writelog. + * Cantera saves a stack of exceptions that it + * has caught in the Application class. This routine writes + * out all of the error messages + * and then clears them from internal storage. + * + * @ingroup errorhandling + */ + void logErrors() { + pMessenger->logErrors() ; + } + + //! Add a directory to the data file search path. + /*! + * @ingroup inputfiles + * + * @param dir String name for the directory to be added to the search path + */ + void addDataDirectory(std::string dir) ; + + //! Find an input file. + /*! + * This routine will search for a file in the default + * locations specified for the application. + * See the routine setDefaultDirectories() listed above. + * + * The default set of directories specified for the application + * will be searched if a '/' or an '\\' is found in the + * name. If either is found then a relative path name is + * presumed, and the default directories are not searched. + * + * The presence of the file is determined by whether the file + * can be opened for reading by the current user. + * + * @param name Name of the input file to be searched for + * + * @return + * + * The absolute path name of the first matching + * file is returned. If a relative path name + * is indicated, the relative path name is returned. + * + * If the file is not found, a message is written to + * stdout and a CanteraError exception is thrown. + * + * @ingroup inputfiles + */ + std::string findInputFile(std::string name) ; + + //! Return a pointer to the XML tree for a Cantera input file. + /*! + * This routine will find the file and read the XML file into an + * XML tree structure. Then, a pointer will be returned. If the + * file has already been processed, then just the pointer will + * be returned. + * + * @param file String containing the relative or absolute file name + * @param debug Debug flag + */ + XML_Node* get_XML_File(std::string file, int debug=0) ; + + //! Close an XML File + /*! + * Close a file that is opened by this application object + * + * @param file String containing the relative or absolute file name + */ + void close_XML_File(std::string file) ; + + //! Write a message to the screen. + /*! + * The string may be of any + * length, and may contain end-of-line characters. This method is + * used throughout %Cantera to write log messages. + * + * @param msg c++ string to be written to the screen + * @ingroup textlogs + */ +#ifdef _WIN32 + long int readStringRegistryKey(const std::string& keyName, const std::string& valueName, + std::string& value, const std::string& defaultValue); +#endif + + void writelog(const std::string& msg) { + pMessenger->writelog(msg); + } + + + //! Write an endl to the screen and flush output + /*! + * @ingroup textlogs + */ + void writelogendl() { + pMessenger->writelogendl(); + } + + //! Write a message to the screen. + /*! + * The string may be of any + * length, and may contain end-of-line characters. This method is + * used throughout %Cantera to write log messages. + * + * @param pszmsg c null terminated string to be written to the screen + * @ingroup textlogs + */ + void writelog(const char* pszmsg) { + pMessenger->writelog(pszmsg); + } + + //! Write an error message and quit. + /*! + * The default behavior is + * to write to the standard error stream, and then call + * exit(). Note that no end-of-line character is appended to + * the message, and so if one is desired it must be included + * in the string. Note that this default behavior will + * terminate the application Cantera is invoked from (MATLAB, + * Excel, etc.) If this is not desired, then derive a class + * and reimplement this method. + * + * @param msg Error message to be written to cerr. + */ + void logerror(const std::string& msg) { + pMessenger->logerror(msg); + } + + //! Returns an integer specifying the application environment. + int getUserEnv() { + return pMessenger->getUserEnv() ; + } + + //! Install a logger - Called by the language interfaces to install an + //! appropriate logger. + /*! + * @param logwriter Pointer to a logger object + * @see Logger. + * @ingroup textlogs + */ + void setLogger(Logger* logwriter) { + pMessenger->setLogger(logwriter); + } + + //! Delete Messenger object allocated per thread. + void thread_complete() ; + +#ifdef WITH_HTML_LOGS + //!Create a new group for log messages. + /*! + * Usually this is called + * upon entering the function, with the title parameter equal to + * the name of the function or method. Subsequent messages + * written with addLogEntry will appear grouped under this + * heading, until endLogGroup() is called. + * + * @param title String name of the LogGroup + * @param loglevel loglevel of the group. + * @ingroup HTML_logs + */ + void beginLogGroup(std::string title, int loglevel) { + pMessenger->beginLogGroup(title,loglevel); + } + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, std::string value) { + pMessenger->addLogEntry(tag, value); + } + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, doublereal value) { + pMessenger->addLogEntry(tag, value); + } + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "tag:value". + * + * @param tag tag + * @param value double value + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string tag, int value) { + pMessenger->addLogEntry(tag, value); + } + + //! Add an entry to an HTML log file. + /*! + * Entries appear in the form "msg". + * + * @param msg Message to be added to file + * + * @ingroup HTML_logs + */ + void addLogEntry(std::string msg) { + pMessenger->addLogEntry(msg); + } + + //! Close the current group of log messages. + /*! + * This is typically + * called just before leaving a function or method, to close the + * group of messages that were output from this + * function. Subsequent messages written with addLogEntry() will + * appear at the next-higher level in the outline, unless + * beginLogGroup() is called first to create a new group. + * + * @param title Name of the log group. It defaults to the most recent + * log group created. + * @ingroup HTML_logs + */ + void endLogGroup(std::string title) { + pMessenger->endLogGroup(title) ; + } + + //! Write the HTML log file. + /*! + * Log entries are stored in memory in + * an XML tree until this function is called, which writes the + * tree to a file and clears the entries stored in memory. The + * output file will have the name specified in the 'file' + * argument. If this argument has no extension, the extension + * '.html' will be appended. Also, if the file already exists, an + * integer will be appended to the name so that no existing log + * file will be overwritten. + * WITH_HTML_LOGS must be defined. + * + * @param file Name of the file to be written + */ + void write_logfile(std::string file) { + pMessenger->write_logfile(file) ; + } +#endif + +protected: + //! Set the default directories for input files. + /*! + * %Cantera searches + * for input files along a path that includes platform-specific + * default locations, and possibly user-specified locations. This + * function installs the platform-specific directories on the + * search path. It is invoked at startup by appinit(), and never + * should need to be called by user programs. + * + * The current directory (".") is always searched first. Then, on + * Windows platforms, if environment variable COMMONPROGRAMFILES + * is set (which it should be on Win XP or Win 2000), then + * directories under this one will be added to the search + * path. The %Cantera Windows installer installs data files to this + * location. + * + * On the Mac, directory '/Applications/Cantera/data' is added to the + * search path. + * + * On any platform, if environment variable CANTERA_DATA is set to a + * directory name, then this directory is added to the search path. + * + * Finally, the location where the data files were installed when + * %Cantera was built is added to the search path. + * + * Additional directories may be added by calling function addDirectory. + * @ingroup inputfiles + */ + void setDefaultDirectories(); + + //! Current vector of input directories to search for input files + std::vector inputDirs; + //! Current list of error messages + //vector errorMessage; + //! Current list of warning messages + //vector warning; + //! Current error Routine + //vector errorRoutine; + //! Last error message + //string msglog; + //! Current line length + // size_t linelen; + //! Current value of stop_on_error + bool stop_on_error; + //! Current map of options + std::map options; + //! Current value of tmp_dir + std::string tmp_dir; + //! Current vector of xml file trees that have been previously parsed + std::map xmlfiles; + //! Current sleep command. + std::string m_sleep; + //! Current pointer to the logwriter + //Logger* logwriter; +#ifdef WITH_HTML_LOGS + //! Current pointer to the top of the XML_Node tree for the current HTML log + //XML_Node *xmllog; + //! Pointer to the last current position in the XML_Node tree for the current HTML log + //XML_Node *current; + //! Current value of loglevel + //int loglevel; + //! Vector of loglevels for loggroups that are open + //vector loglevels; + //! Current vector of loggroups that are open + //vector loggroups; +#endif + +#if defined(THREAD_SAFE_CANTERA) + ThreadMessages pMessenger ; +#else + std::auto_ptr pMessenger ; +#endif + +private: + //! Pointer to the single Application instance + static Application* s_app ; +}; + +} + +#endif diff --git a/src/base/ctexceptions.cpp b/src/base/ctexceptions.cpp new file mode 100644 index 000000000..bccf493b7 --- /dev/null +++ b/src/base/ctexceptions.cpp @@ -0,0 +1,48 @@ +#include "cantera/base/ctexceptions.h" + +#include "application.h" +#include "cantera/base/global.h" +#include "cantera/base/stringUtils.h" + +namespace Cantera { + +// *** Exceptions *** + +CanteraError::CanteraError(std::string procedure, std::string msg) +{ + Application::Instance()->addError(procedure, msg); +} + +ArraySizeError::ArraySizeError(std::string proc, size_t sz, size_t reqd) : + CanteraError(proc, "Array size ("+int2str(sz)+ + ") too small. Must be at least "+int2str(reqd)) +{ +} + + +ElementRangeError::ElementRangeError(std::string func, size_t m, size_t mmax) : + CanteraError(func, "Element index " + int2str(m) + + " outside valid range of 0 to " + int2str(mmax-1)) +{ +} + +// *** Warnings *** + +void deprecatedMethod(std::string classnm, std::string oldnm, std::string newnm) +{ + writelog(">>>> WARNING: method "+oldnm+" of class "+classnm + +" is deprecated.\n"); + writelog(" Use method "+newnm+" instead.\n"); + writelog(" (If you want to rescue this method from deprecated\n"); + writelog(" status, see http://www.cantera.org/deprecated.html)"); +} + +void removeAtVersion(std::string func, std::string version) +{ + writelog("Removed procedure: "+func+"\n"); + writelog("Removed in version: "+version+"\n"); + throw CanteraError("removeAtVersion: "+ func,"procedure has been removed."); +} + + +} // namespace Cantera diff --git a/src/base/global.cpp b/src/base/global.cpp new file mode 100644 index 000000000..0afd5816c --- /dev/null +++ b/src/base/global.cpp @@ -0,0 +1,303 @@ +#include "cantera/base/global.h" + +#include "cantera/base/ctexceptions.h" +#include "cantera/base/FactoryBase.h" +#include "cantera/base/xml.h" +#include "application.h" +#include "units.h" + +// RFB:TODO May need OS specifc include to use varargs.h instead +#include + +using namespace std; + +namespace Cantera { + +//! Return a pointer to the application object +static Application* app() +{ + return Application::Instance() ; +} + +// **************** Text Logging **************** + +void setLogger(Logger* logwriter) +{ + try { + app()->setLogger(logwriter) ; + } catch (std::bad_alloc) { + logwriter->error("bad alloc thrown by app()"); + } +} + +void writelog(const std::string& msg) +{ + app()->writelog(msg); +} + +void writelog(const char* msg) +{ + app()->writelog(msg); +} + +void writelogf(const char* fmt,...) +{ + enum { BUFSIZE = 2048 } ; + char sbuf[BUFSIZE] ; + + va_list args ; + va_start(args, fmt) ; + +#ifdef _MSC_VER + _vsnprintf(sbuf, BUFSIZE, fmt, args) ; +#else + vsprintf(sbuf, fmt, args) ; +#endif + + writelog(sbuf) ; + va_end(args) ; +} + +void writelogendl() +{ + app()->writelogendl(); +} + +void error(const std::string& msg) +{ + app()->logerror(msg); +} + +int userInterface() +{ + return app()->getUserEnv(); +} + +// **************** HTML Logging **************** + +#ifdef WITH_HTML_LOGS + +void beginLogGroup(std::string title, int loglevel) +{ + app()->beginLogGroup(title, loglevel) ; +} + +void addLogEntry(std::string tag, std::string value) +{ + app()->addLogEntry(tag, value) ; +} + +void addLogEntry(std::string tag, doublereal value) +{ + app()->addLogEntry(tag, value) ; +} + +void addLogEntry(std::string tag, int value) +{ + app()->addLogEntry(tag, value) ; +} + +void addLogEntry(std::string msg) +{ + app()->addLogEntry(msg) ; +} + +void endLogGroup(std::string title) +{ + app()->endLogGroup(title) ; +} + +void write_logfile(std::string file) +{ + app()->write_logfile(file) ; +} + +#endif // WITH_HTML_LOGS + +// **************** Global Data **************** + +Unit* Unit::s_u = 0; + +#if defined(THREAD_SAFE_CANTERA) +boost::mutex Unit::units_mutex ; +#endif + +void appdelete() +{ + Application::ApplicationDestroy() ; + FactoryBase::deleteFactories(); + Unit::deleteUnit(); +} + +void thread_complete() +{ + app()->thread_complete() ; +} + +XML_Node* get_XML_File(std::string file, int debug) +{ + XML_Node* xtmp = app()->get_XML_File(file, debug) ; + //writelog("get_XML_File: returned from app:get_XML_FILE " + int2str(xtmp) + "\n"); + return xtmp; +} + +void close_XML_File(std::string file) +{ + app()->close_XML_File(file) ; +} + +void setTmpDir(std::string tmp) +{ + app()->setTmpDir(tmp); +} + +string tmpDir() +{ + return app()->getTmpDir(); +} + +string sleep() +{ + return app()->sleep(); +} + +int nErrors() +{ + return app()->getErrorCount(); +} + +void popError() +{ + app()->popError() ; +} + +string lastErrorMessage() +{ + return app()->lastErrorMessage() ; +} + +void showErrors(std::ostream& f) +{ + app()->getErrors(f) ; +} + +void showErrors() +{ + app()->logErrors() ; +} + +void setError(std::string r, std::string msg) +{ + app()->addError(r, msg) ; +} + +void addDirectory(std::string dir) +{ + app()->addDataDirectory(dir) ; +} + +std::string findInputFile(std::string name) +{ + return app()->findInputFile(name) ; +} + +doublereal toSI(std::string unit) +{ + doublereal f = Unit::units()->toSI(unit); + if (f) { + return f; + } else { + throw CanteraError("toSI","unknown unit string: "+unit); + } + return 1.0; +} + +doublereal actEnergyToSI(std::string unit) +{ + doublereal f = Unit::units()->actEnergyToSI(unit); + if (f) { + return f; + } + return 1.0; +} + +string canteraRoot() +{ + char* ctroot = 0; + ctroot = getenv("CANTERA_ROOT"); + if (ctroot != 0) { + return string(ctroot); + } +#ifdef CANTERA_ROOT + return string(CANTERA_ROOT); +#else + return ""; +#endif + +} + +//! split a string at a '#' sign. Used to separate a file name from an id string. +/*! + * @param src Original string to be split up. This is unchanged. + * @param file Output string representing the first part of the string, which is the filename. + * @param id Output string representing the last part of the string, which is the id. + */ +static void split_at_pound(const std::string& src, std::string& file, std::string& id) +{ + string::size_type ipound = src.find('#'); + if (ipound != string::npos) { + id = src.substr(ipound+1,src.size()); + file = src.substr(0,ipound); + } else { + id = ""; + file = src; + } +} + +XML_Node* get_XML_Node(const std::string& file_ID, XML_Node* root) +{ + std::string fname, idstr; + XML_Node* db, *doc; + split_at_pound(file_ID, fname, idstr); + if (fname == "") { + if (!root) throw CanteraError("get_XML_Node", + "no file name given. file_ID = "+file_ID); + db = root->findID(idstr, 3); + } else { + doc = get_XML_File(fname); + if (!doc) throw CanteraError("get_XML_Node", + "get_XML_File failed trying to open "+fname); + db = doc->findID(idstr, 3); + } + if (!db) { + throw CanteraError("get_XML_Node", + "id tag '"+idstr+"' not found."); + } + return db; +} + +XML_Node* get_XML_NameID(const std::string& nameTarget, + const std::string& file_ID, + XML_Node* root) +{ + string fname, idTarget; + XML_Node* db, *doc; + split_at_pound(file_ID, fname, idTarget); + if (fname == "") { + if (!root) { + return 0; + } + db = root->findNameID(nameTarget, idTarget); + } else { + doc = get_XML_File(fname); + if (!doc) { + return 0; + } + db = doc->findNameID(nameTarget, idTarget); + } + return db; +} + +std::vector FactoryBase::s_vFactoryRegistry; + +} diff --git a/src/base/misc.cpp b/src/base/misc.cpp deleted file mode 100644 index f2017f4ef..000000000 --- a/src/base/misc.cpp +++ /dev/null @@ -1,2153 +0,0 @@ -/** - * @file misc.cpp - * This file contains a miscellaneous collection of global data - * functions and text for the globalData module (see \ref globalData), the - * logs module (see \ref logs), and the HTML_logs module (see \ref HTML_logs). - * - * These modules are defined here: - * globalData - * logs - * HTML_logs - */ - -// RFB:TODO May need OS specifc include to use varargs.h instead -#include - -#include "cantera/base/global.h" -#include "cantera/base/ctexceptions.h" -#include "cantera/base/stringUtils.h" -#include "units.h" -#include "cantera/base/xml.h" -#include "cantera/base/ctml.h" -#include "cantera/base/FactoryBase.h" -#include "cantera/base/logger.h" - -#include -#include - -#ifdef _WIN32 -#include -#include -#include -#include -#endif - -#ifdef _MSC_VER -#pragma comment(lib, "advapi32") -#endif - -using namespace std; - -// RFB : If running multiple threads in a cpp application, -// the Application class is the only internal object -// that is single instance with static data. Synchronize access to those data -// structures. -// Using macros to avoid polluting code with alot of ifdef's - -#ifdef THREAD_SAFE_CANTERA - -#include -#include -//! Mutex for input directory access -static boost::mutex dir_mutex; - -//! Mutex for access to string messages -static boost::mutex msg_mutex; - -//! Mutex for creating singeltons within the application object -static boost::mutex app_mutex; - -// Mutex for controlling access to the log file -//static boost::mutex log_mutex; - -//! Mutex for controlling access to XML file storage -static boost::mutex xml_mutex; - -//! Macro for locking input directory access -#define DIR_LOCK() boost::mutex::scoped_lock d_lock(dir_mutex) - -//! Macro for locking access to string messages -#define MSG_LOCK() boost::mutex::scoped_lock m_lock(msg_mutex) - -//! Macro for locking creating singletons in the application state -#define APP_LOCK() boost::mutex::scoped_lock a_lock(app_mutex) - -//! Macro for locking XML file writing -#define XML_LOCK() boost::mutex::scoped_lock x_lock(xml_mutex) - -#ifdef WITH_HTML_LOGS -//static boost::mutex html_mutex; // html logs -//#define HTML_LOCK() boost::mutex::scoped_lock h_lock(html_mutex) -#endif - -#if defined(BOOST_HAS_WINTHREADS) -#include -typedef unsigned int cthreadId_t ; -class thread_equal -{ -public: - bool operator()(cthreadId_t L, cthreadId_t R) { - return L == R ; - } -} ; -cthreadId_t getThisThreadId() -{ - return ::GetCurrentThreadId() ; -} -#elif defined(BOOST_HAS_PTHREADS) - -typedef pthread_t cthreadId_t ; -class thread_equal -{ -public: - bool operator()(cthreadId_t L, cthreadId_t R) { - return pthread_equal(L, R) ; - } -} ; -cthreadId_t getThisThreadId() -{ - return pthread_self() ; -} -#elif defined(BOOST_HAS_MPTASKS) -typedef MPTaskID cthreadId_t ; -class thread_equal -{ -public: - bool operator()(cthreadId_t L, cthreadId_t R) { - return L == R ; - } -} ; -cthreadId_t getThisThreadId() -{ - return MPCurrentTaskID() ; -} -#endif - -#else -#define DIR_LOCK() -#define MSG_LOCK() -#define APP_LOCK() -//#define LOG_LOCK() -#define XML_LOCK() - -#ifdef WITH_HTML_LOGS -//#define HTML_LOCK() -#endif - -#endif - -namespace Cantera -{ - -/*! - * @defgroup globalData Global Data - * - * Global data are available anywhere. There are two kinds. - * Cantera has an assortment of constant values for physical parameters. - * Also, Cantera maintains a collection of global data which is specific - * to each process that invokes Cantera functions. This process-specific - * data is stored in the class Application. - */ -//@{ -//@} - -//! Class to hold global data. -/*! - * Class Application is the top-level - * class that stores data that should persist for the duration of - * the process. The class should not be instantiated directly; - * instead, it is instantiated as needed by the functions declared - * here. At most one instance is created, and it is not destroyed - * until the process terminates. - * - * @ingroup HTML_logs - * @ingroup textlogs - * @ingroup globalData - */ -class Application -{ - -protected: - //! Class to carry out messages - /*! - * @ingroup HTML_logs - */ - class Messages - { - public: - //! Constructor for the Messages class - /*! - * constructor for the Messages class which is a subclass - * of the Application class. - */ - Messages() : - errorMessage(0), - errorRoutine(0), - logwriter(0) -#ifdef WITH_HTML_LOGS - ,xmllog(0), - current(0), - loglevel(0), - loglevels(0), - loggroups(0) -#endif - { - // install a default logwriter that writes to standard - // output / standard error - logwriter = new Logger(); - } - - //! Copy Constructor for the Messages class - /*! - * Constructor for the Messages class which is a subclass - * of the Application class. - * @param r Message to be copied - */ - Messages(const Messages& r) : - errorMessage(r.errorMessage), - errorRoutine(r.errorRoutine), - logwriter(0) -#ifdef WITH_HTML_LOGS - , xmllog(r.xmllog), - current(r.current), - loglevel(r.loglevel), - loglevels(r.loglevels), - loggroups(r.loggroups) -#endif - { - // install a default logwriter that writes to standard - // output / standard error - logwriter = new Logger(*(r.logwriter)); - } - - //! Assignment operator - /*! - * @param r Message to be copied - */ - Messages& operator=(const Messages& r) { - if (this == &r) { - return *this; - } - errorMessage = r.errorMessage; - errorRoutine = r.errorRoutine; - logwriter = new Logger(*(r.logwriter)); -#ifdef WITH_HTML_LOGS - xmllog = r.xmllog; - current = r.current; - loglevel = r.loglevel; - loglevels = r.loglevels; - loggroups = r.loggroups; -#endif - return *this; - } - - //! Destructor for the Messages class - ~Messages() { - delete logwriter; -#ifdef WITH_HTML_LOGS - if (xmllog) { - write_logfile("orphan"); - } -#endif - } - - //! Set an error condition in the application class without - //! throwing an exception. - /*! - * This routine adds an error message to the end of the stack - * of errors that Cantera accumulates in the Application - * class. - * @param r location - * @param msg Description of the error - * @ingroup errorhandling - */ - void addError(std::string r, std::string msg) ; - - //! Return the number of errors that have been encountered so far. - /*! - * @ingroup errorhandling - */ - int getErrorCount() ; - - //! Discard the last error message - /*! - * %Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine eliminates - * the last exception to be added to that stack. - * - * @ingroup errorhandling - */ - void popError() ; - - //! Retrieve the last error message in a string - /*! - * This routine will retrieve the last error message and return - * it in the return string. - * - * @ingroup errorhandling - */ - std::string lastErrorMessage() ; - - //! Prints all of the error messages to an ostream - /*! - * Print all of the error messages using function writelog. - * Write out all of the saved error messages to the ostream f - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages to the ostream - * and then clears them from internal storage. - * - * @param f ostream which will receive the error messages - * - * @ingroup errorhandling - */ - void getErrors(std::ostream& f) ; - - //! Prints all of the error messages using writelog - /*! - * Print all of the error messages using function writelog. - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages - * and then clears them from internal storage. - * - * @ingroup errorhandling - */ - void logErrors(); - - //! Write a message to the screen. - /*! - * The string may be of any - * length, and may contain end-of-line characters. This method is - * used throughout %Cantera to write log messages. - * - * @param msg c++ string to be written to the screen - * @ingroup textlogs - */ - void writelog(const std::string& msg); - - //! Write an end of line and flush output - void writelogendl(); - - //! Write a message to the screen. - /*! - * The string may be of any - * length, and may contain end-of-line characters. This method is - * used throughout %Cantera to write log messages. - * - * @param pszmsg c character string to be written to the screen - * @ingroup textlogs - */ - void writelog(const char* pszmsg) ; - - //! Write an error message and quit. - /*! - * The default behavior is - * to write to the standard error stream, and then call - * exit(). Note that no end-of-line character is appended to - * the message, and so if one is desired it must be included - * in the string. Note that this default behavior will - * terminate the application Cantera is invoked from (MATLAB, - * Excel, etc.) If this is not desired, then derive a class - * and reimplement this method. - * - * @param msg Error message to be written to cerr. - */ - void logerror(const std::string& msg) ; - - //! Returns an integer specifying the application environment - int getUserEnv() ; - - //! Install a logger. - /*! - * Called by the language interfaces to install an appropriate logger. - * The logger is used for the writelog() function - * - * @param logwriter Pointer to a logger object - * @see Logger. - * @ingroup textlogs - */ - void setLogger(Logger* logwriter) ; - -#ifdef WITH_HTML_LOGS - - //!Create a new group for log messages. - /*! - * Usually this is called - * upon entering the function, with the title parameter equal to - * the name of the function or method. Subsequent messages - * written with addLogEntry will appear grouped under this - * heading, until endLogGroup() is called. - * - * @param title String name of the LogGroup - * @param loglevel loglevel of the group. - * @ingroup HTML_logs - */ - void beginLogGroup(std::string title, int loglevel) ; - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, std::string value) ; - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, doublereal value) ; - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, int value) ; - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "msg". - * - * @param msg Message to be added - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string msg) ; - - //! Close the current group of log messages. - /*! - * This is typically - * called just before leaving a function or method, to close the - * group of messages that were output from this - * function. Subsequent messages written with addLogEntry() will - * appear at the next-higher level in the outline, unless - * beginLogGroup() is called first to create a new group. - * - * @param title Name of the log group. It defaults to the most recent - * log group created. - */ - void endLogGroup(std::string title) ; - - //! Write the HTML log file. - /*! - * Log entries are stored in memory in - * an XML tree until this function is called, which writes the - * tree to a file and clears the entries stored in memory. The - * output file will have the name specified in the 'file' - * argument. If this argument has no extension, the extension - * '.html' will be appended. Also, if the file already exists, an - * integer will be appended to the name so that no existing log - * file will be overwritten. - * WITH_HTML_LOGS must be defined. - * - * @param file Name of the file to be written - */ - void write_logfile(std::string file); -#endif - - protected: - //! Current list of error messages - vector errorMessage; - - //! Current error Routine - vector errorRoutine; - - //! Current pointer to the logwriter - Logger* logwriter; -#ifdef WITH_HTML_LOGS - //! Current pointer to the top of the XML_Node tree for the current HTML log - XML_Node* xmllog; - - //! Pointer to the last current position in the XML_Node tree for the current HTML log - XML_Node* current; - - //! Current value of the loglevel - int loglevel; - - //! Vector of loglevels for loggroups that are open - vector loglevels; - - //! Current vector of loggroups that are open - vector loggroups; -#endif - } ; - -#ifdef THREAD_SAFE_CANTERA - //! Typedef for thread specific messages - typedef boost::shared_ptr< Messages > pMessages_t ; - - //! Typedef for map between a thread and the message - typedef std::map< cthreadId_t, pMessages_t > threadMsgMap_t ; - - //! Class that stores thread messages for each thread, and retrieves them - //! based on the thread id. - class ThreadMessages - { - public: - //! Constructor - ThreadMessages() { - } - - //! Provide a pointer deferencing overloaded operator - /*! - * @return returns a pointer to Messages - */ - Messages* operator->() { - MSG_LOCK() ; - cthreadId_t curId = getThisThreadId() ; - threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ; - if (iter != m_threadMsgMap.end()) { - return (iter->second.get()) ; - } - pMessages_t pMsgs(new Messages()) ; - m_threadMsgMap.insert(std::pair< cthreadId_t, pMessages_t >(curId, pMsgs)) ; - return pMsgs.get() ; - } - - //! Remove a local thread message - void removeThreadMessages() { - MSG_LOCK() ; - cthreadId_t curId = getThisThreadId() ; - threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ; - if (iter != m_threadMsgMap.end()) { - m_threadMsgMap.erase(iter) ; - } - } - - private: - //! Thread Msg Map - threadMsgMap_t m_threadMsgMap ; - } ; -#endif - - -protected: //RFB Protected ctor access thru static member function Instance - //! Constructor for class sets up the initial conditions - Application() : - inputDirs(0), - stop_on_error(false), - options(), - tmp_dir("."), - xmlfiles(), - m_sleep("1"), - pMessenger() { -#if !defined( THREAD_SAFE_CANTERA ) - pMessenger = std::auto_ptr(new Messages()); -#endif - // if TMP or TEMP is set, use it for the temporary - // directory - char* ctmpdir = getenv("CANTERA_TMPDIR"); - if (ctmpdir != 0) { - tmp_dir = string(ctmpdir); - } else { - char* tmpdir = getenv("TMP"); - if (tmpdir == 0) { - tmpdir = getenv("TEMP"); - } - if (tmpdir != 0) { - tmp_dir = string(tmpdir); - } - } - - // if SLEEP is set, use it as the sleep time - char* sleepstr = getenv("SLEEP"); - if (sleepstr != 0) { - m_sleep = string(sleepstr); - } - - // install a default logwriter that writes to standard - // output / standard error - // logwriter = new Logger(); - //#ifdef WITH_HTML_LOGS - // // HTML log files - // xmllog = 0; - // current = 0; - // loglevel = 0; - //#endif - setDefaultDirectories(); -#if defined(THREAD_SAFE_CANTERA) - Unit::units() ; -#endif - } - -public: - //! Return a pointer to the one and only instance of class Application - /* - * If the an Application object has not yet been created it is created - */ - static Application* Instance() { - APP_LOCK(); - if (Application::s_app == 0) { - Application::s_app = new Application(); - } - return s_app; - } - - //! Destructor for class deletes global data - /*! - * Delete any open XML trees, the logwriter, and - * the XML log, if any. - */ - virtual ~Application() { - std::map::iterator pos; - for (pos = xmlfiles.begin(); pos != xmlfiles.end(); ++pos) { - pos->second->unlock(); - delete pos->second; - pos->second = 0; - } - } - - //! Static function that destroys the application class's data - static void ApplicationDestroy() { - APP_LOCK() ; - if (Application::s_app != 0) { - delete Application::s_app; - Application::s_app = 0; - } - } - - //! Set the directory where temporary files may be written. - /*! - * @param tmp Name of the directory - */ - void setTmpDir(std::string tmp) ; - - //! get the name of a directory where temporary files may be written. - std::string getTmpDir() ; - - //! Pause the application for a certain amount - /*! - * This has been needed in the past for some network file systems. - */ - std::string sleep() ; - - //! Set an error condition in the application class without - //! throwing an exception. - /*! - * This routine adds an error message to the end of the stack - * of errors that Cantera accumulates in the Application - * class. - * @param r location - * @param msg Description of the error - * @ingroup errorhandling - */ - void addError(std::string r, std::string msg) { - pMessenger->addError(r, msg) ; - } - - //! Return the number of errors that have been encountered so far. - /*! - * @ingroup error handling - */ - int getErrorCount() { - return pMessenger->getErrorCount() ; - } - - //! Discard the last error message - /*! - * %Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine eliminates - * the last exception to be added to that stack. - * - * @ingroup errorhandling - */ - void popError() { - pMessenger->popError() ; - } - - //! Retrieve the last error message in a string - /*! - * This routine will retrieve the last error message and return - * it in the return string. - * - * @ingroup errorhandling - */ - std::string lastErrorMessage() { - return pMessenger->lastErrorMessage() ; - } - - //! Prints all of the error messages to an ostream - /*! - * Print all of the error messages using function writelog. - * Write out all of the saved error messages to the ostream f - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages to the ostream - * and then clears them from internal storage. - * - * @param f ostream which will receive the error messages - * - * @ingroup errorhandling - */ - void getErrors(std::ostream& f) { - pMessenger->getErrors(f) ; - } - - //! Prints all of the error messages using writelog - /*! - * Print all of the error messages using function writelog. - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages - * and then clears them from internal storage. - * - * @ingroup errorhandling - */ - void logErrors() { - pMessenger->logErrors() ; - } - - //! Add a directory to the data file search path. - /*! - * @ingroup inputfiles - * - * @param dir String name for the directory to be added to the search path - */ - void addDataDirectory(std::string dir) ; - - //! Find an input file. - /*! - * This routine will search for a file in the default - * locations specified for the application. - * See the routine setDefaultDirectories() listed above. - * - * The default set of directories specified for the application - * will be searched if a '/' or an '\\' is found in the - * name. If either is found then a relative path name is - * presumed, and the default directories are not searched. - * - * The presence of the file is determined by whether the file - * can be opened for reading by the current user. - * - * @param name Name of the input file to be searched for - * - * @return - * - * The absolute path name of the first matching - * file is returned. If a relative path name - * is indicated, the relative path name is returned. - * - * If the file is not found, a message is written to - * stdout and a CanteraError exception is thrown. - * - * @ingroup inputfiles - */ - std::string findInputFile(std::string name) ; - - //! Return a pointer to the XML tree for a Cantera input file. - /*! - * This routine will find the file and read the XML file into an - * XML tree structure. Then, a pointer will be returned. If the - * file has already been processed, then just the pointer will - * be returned. - * - * @param file String containing the relative or absolute file name - * @param debug Debug flag - */ - XML_Node* get_XML_File(std::string file, int debug=0) ; - - //! Close an XML File - /*! - * Close a file that is opened by this application object - * - * @param file String containing the relative or absolute file name - */ - void close_XML_File(std::string file) ; - - //! Write a message to the screen. - /*! - * The string may be of any - * length, and may contain end-of-line characters. This method is - * used throughout %Cantera to write log messages. - * - * @param msg c++ string to be written to the screen - * @ingroup textlogs - */ -#ifdef _WIN32 - long int readStringRegistryKey(const std::string& keyName, const std::string& valueName, - std::string& value, const std::string& defaultValue); -#endif - - void writelog(const std::string& msg) { - pMessenger->writelog(msg); - } - - - //! Write an endl to the screen and flush output - /*! - * @ingroup textlogs - */ - void writelogendl() { - pMessenger->writelogendl(); - } - - //! Write a message to the screen. - /*! - * The string may be of any - * length, and may contain end-of-line characters. This method is - * used throughout %Cantera to write log messages. - * - * @param pszmsg c null terminated string to be written to the screen - * @ingroup textlogs - */ - void writelog(const char* pszmsg) { - pMessenger->writelog(pszmsg); - } - - //! Write an error message and quit. - /*! - * The default behavior is - * to write to the standard error stream, and then call - * exit(). Note that no end-of-line character is appended to - * the message, and so if one is desired it must be included - * in the string. Note that this default behavior will - * terminate the application Cantera is invoked from (MATLAB, - * Excel, etc.) If this is not desired, then derive a class - * and reimplement this method. - * - * @param msg Error message to be written to cerr. - */ - void logerror(const std::string& msg) { - pMessenger->logerror(msg); - } - - //! Returns an integer specifying the application environment. - int getUserEnv() { - return pMessenger->getUserEnv() ; - } - - //! Install a logger - Called by the language interfaces to install an - //! appropriate logger. - /*! - * @param logwriter Pointer to a logger object - * @see Logger. - * @ingroup textlogs - */ - void setLogger(Logger* logwriter) { - pMessenger->setLogger(logwriter); - } - - //! Delete Messenger object allocated per thread. - void thread_complete() ; - -#ifdef WITH_HTML_LOGS - //!Create a new group for log messages. - /*! - * Usually this is called - * upon entering the function, with the title parameter equal to - * the name of the function or method. Subsequent messages - * written with addLogEntry will appear grouped under this - * heading, until endLogGroup() is called. - * - * @param title String name of the LogGroup - * @param loglevel loglevel of the group. - * @ingroup HTML_logs - */ - void beginLogGroup(std::string title, int loglevel) { - pMessenger->beginLogGroup(title,loglevel); - } - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, std::string value) { - pMessenger->addLogEntry(tag, value); - } - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, doublereal value) { - pMessenger->addLogEntry(tag, value); - } - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "tag:value". - * - * @param tag tag - * @param value double value - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string tag, int value) { - pMessenger->addLogEntry(tag, value); - } - - //! Add an entry to an HTML log file. - /*! - * Entries appear in the form "msg". - * - * @param msg Message to be added to file - * - * @ingroup HTML_logs - */ - void addLogEntry(std::string msg) { - pMessenger->addLogEntry(msg); - } - - //! Close the current group of log messages. - /*! - * This is typically - * called just before leaving a function or method, to close the - * group of messages that were output from this - * function. Subsequent messages written with addLogEntry() will - * appear at the next-higher level in the outline, unless - * beginLogGroup() is called first to create a new group. - * - * @param title Name of the log group. It defaults to the most recent - * log group created. - * @ingroup HTML_logs - */ - void endLogGroup(std::string title) { - pMessenger->endLogGroup(title) ; - } - - //! Write the HTML log file. - /*! - * Log entries are stored in memory in - * an XML tree until this function is called, which writes the - * tree to a file and clears the entries stored in memory. The - * output file will have the name specified in the 'file' - * argument. If this argument has no extension, the extension - * '.html' will be appended. Also, if the file already exists, an - * integer will be appended to the name so that no existing log - * file will be overwritten. - * WITH_HTML_LOGS must be defined. - * - * @param file Name of the file to be written - */ - void write_logfile(std::string file) { - pMessenger->write_logfile(file) ; - } -#endif - -protected: - void setDefaultDirectories(); - - //! Current vector of input directories to search for input files - vector inputDirs; - //! Current list of error messages - //vector errorMessage; - //! Current list of warning messages - //vector warning; - //! Current error Routine - //vector errorRoutine; - //! Last error message - //string msglog; - //! Current line length - // size_t linelen; - //! Current value of stop_on_error - bool stop_on_error; - //! Current map of options - map options; - //! Current value of tmp_dir - string tmp_dir; - //! Current vector of xml file trees that have been previously parsed - map xmlfiles; - //! Current sleep command. - string m_sleep; - //! Current pointer to the logwriter - //Logger* logwriter; -#ifdef WITH_HTML_LOGS - //! Current pointer to the top of the XML_Node tree for the current HTML log - //XML_Node *xmllog; - //! Pointer to the last current position in the XML_Node tree for the current HTML log - //XML_Node *current; - //! Current value of loglevel - //int loglevel; - //! Vector of loglevels for loggroups that are open - //vector loglevels; - //! Current vector of loggroups that are open - //vector loggroups; -#endif - -#if defined(THREAD_SAFE_CANTERA) - ThreadMessages pMessenger ; -#else - auto_ptr< Messages > pMessenger ; -#endif - -private: - static Application* s_app ; -}; - -//! Return a pointer to the application object -inline Application* app() -{ - return Application::Instance() ; -} - - -/// Pointer to the single Application instance -Application* Application::s_app = 0; - -/** - * Definition of the static member of the Unit class. - */ -Unit* Unit::s_u = 0; -#if defined(THREAD_SAFE_CANTERA) -boost::mutex Unit::units_mutex ; -#endif - - -/* - * Delete all global data. It should be called at the end of the - * application if leak checking is to be done. - */ -void appdelete() -{ - Application::ApplicationDestroy() ; - FactoryBase::deleteFactories(); - Unit::deleteUnit(); -} - -/* - * Delete Messenger object allocated per thread. - */ -void thread_complete() -{ - app()->thread_complete() ; -} - -void Application::thread_complete() -{ -#if defined(THREAD_SAFE_CANTERA) - pMessenger.removeThreadMessages() ; -#endif -} - -XML_Node* get_XML_File(std::string file, int debug) -{ - XML_Node* xtmp = app()->get_XML_File(file, debug) ; - //writelog("get_XML_File: returned from app:get_XML_FILE " + int2str(xtmp) + "\n"); - return xtmp; -} - -XML_Node* Application::get_XML_File(std::string file, int debug) -{ - XML_LOCK(); - std::string path = ""; - /* - try { - path = findInputFile(file); - } - catch (CanteraError) { - string::size_type idot = file.rfind('.'); - string ext = ""; - if (idot != string::npos) { - ext = file.substr(idot, file.size()); - string ctifile = file.substr(0,idot)+".cti"; - try { - path = findInputFile(ctifile); - } - catch (CanteraError) { - path = findInputFile(file); - } - } - else - path = findInputFile(file); - } - */ - // The code above will try to process a cti file if the xml - // file is not found. But I (dgg) don't think it makes much sense, - // so it is replaced by: - path = findInputFile(file); -#ifdef _WIN32 - // RFB: For Windows make the path POSIX compliant so code looking for directory - // separators is simpler. Just look for '/' not both '/' and '\\' - replace_if(path.begin(), path.end(), bind2nd(equal_to(), '\\'), '/') ; -#endif - - string ff = path; - if (xmlfiles.find(path) - == xmlfiles.end()) { - /* - * Check whether or not the file is XML. If not, it will - * be first processed with the preprocessor. We determine - * whether it is an XML file by looking at the file extension. - */ - string::size_type idot = path.rfind('.'); - string ext; - if (idot != string::npos) { - ext = path.substr(idot, path.size()); - } else { - ext = ""; - idot = path.size(); - } - if (ext != ".xml" && ext != ".ctml") { - /* - * We will assume that we are trying to open a cti file. - * First, determine the name of the xml file, ff, derived from - * the cti file. - * In all cases, we will write the xml file to the current - * directory. - */ - string::size_type islash = path.rfind('/'); - if (islash != string::npos) { - ff = string("./")+path.substr(islash+1,idot-islash - 1) + ".xml"; - } else { - ff = string("./")+path.substr(0,idot) + ".xml"; - } -#ifdef DEBUG_PATHS - cout << "get_XML_File(): Expected location of xml file = " - << ff << endl; -#endif - /* - * Do a search of the existing XML trees to determine if we have - * already processed this file. If we have, return a pointer to - * the processed xml tree. - */ - if (xmlfiles.find(ff) != xmlfiles.end()) { -#ifdef DEBUG_PATHS - cout << "get_XML_File(): File, " << ff << ", was previously read." - << " Retrieving the stored xml tree." << endl; -#endif - return xmlfiles[ff]; - } - /* - * Ok, we didn't find the processed XML tree. Do the conversion - * to xml, possibly overwriting the file, ff, in the process. - */ - ctml::ct2ctml(path.c_str(),debug); - } else { - ff = path; - } - /* - * Take the XML file ff, open it, and process it, creating an - * XML tree, and then adding an entry in the map. We will store - * the absolute pathname as the key for this map. - */ - ifstream s(ff.c_str()); - - XML_Node* x = new XML_Node("doc"); - if (s) { - x->build(s); - x->lock(); - xmlfiles[ff] = x; - } else { - string estring = "cannot open "+ff+" for reading."; - estring += "Note, this error indicates a possible configuration problem."; - throw CanteraError("get_XML_File", estring); - } - } - - /* - * Return the XML node pointer. At this point, we are sure that the - * lookup operation in the return statement will return a valid - * pointer. - */ - return xmlfiles[ff]; -} - -// Close an XML File -/* - * Close a file that is openned by the application object - * - * @param file string name of the file - */ -void close_XML_File(std::string file) -{ - app()->close_XML_File(file) ; -} - -void Application::close_XML_File(std::string file) -{ - XML_LOCK(); - if (file == "all") { - map::iterator - b = xmlfiles.begin(), - e = xmlfiles.end(); - for (; b != e; ++b) { - b->second->unlock(); - delete b->second; - xmlfiles.erase(b->first); - } - } else if (xmlfiles.find(file) != xmlfiles.end()) { - xmlfiles[file]->unlock(); - delete xmlfiles[file]; - xmlfiles.erase(file); - } -} - -#ifdef _WIN32 -long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName, - std::string& value, const std::string& defaultValue) -{ - - HKEY key; - long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key); - if (open_error != ERROR_SUCCESS) { - return open_error; - } - value = defaultValue; - CHAR buffer[1024]; - DWORD bufferSize = sizeof(buffer); - ULONG error; - error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize); - if (ERROR_SUCCESS == error) { - value = buffer; - } - RegCloseKey(key); - return error; -} -#endif - - -void setTmpDir(std::string tmp) -{ - app()->setTmpDir(tmp); -} -void Application::setTmpDir(std::string tmp) -{ - APP_LOCK(); - tmp_dir = tmp ; -} - -string tmpDir() -{ - return app()->getTmpDir(); -} -std::string Application::getTmpDir() -{ - APP_LOCK(); - return tmp_dir ; -} - -string sleep() -{ - return app()->sleep(); -} -std::string Application::sleep() -{ - APP_LOCK(); - return m_sleep ; -} - - -/* - * Return the number of errors that have been encountered so far. - * \ingroup errorhandling - */ -int nErrors() -{ - return app()->getErrorCount(); -} - -// Return the number of errors encountered so far -int Application::Messages::getErrorCount() -{ - return static_cast(errorMessage.size()) ; -} - -/* - * popError eliminates the last error message that Cantera - * has saved. Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine eliminates - * the last exception to be added to that stack. - * \ingroup errorhandling - */ -void popError() -{ - app()->popError() ; -} - -// Eliminate the last error message -void Application::Messages::popError() -{ - if (static_cast(errorMessage.size()) > 0) { - errorRoutine.pop_back() ; - errorMessage.pop_back() ; - } -} - -/* - * Retrieve the last error message. - * This routine will retrieve the last error message and return - * it in the return string. - * \ingroup errorhandling - */ -string lastErrorMessage() -{ - return app()->lastErrorMessage() ; -} - -// Retrieve the last error message -std::string Application::Messages::lastErrorMessage() -{ - if (static_cast(errorMessage.size()) > 0) { - string head = - "\n\n************************************************\n" - " Cantera Error! \n" - "************************************************\n\n"; - return head+string("\nProcedure: ")+errorRoutine.back() - +string("\nError: ")+errorMessage.back(); - } else { - return ""; - } -} - -/* - * Prints all of the error messages to stream f. - * Write out to ostream, f, all of the saved error messages. - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages to ostream f, and then - * clears them from internal storage. - * \ingroup errorhandling - */ -void showErrors(std::ostream& f) -{ - app()->getErrors(f) ; -} - -// Prints all error messages to stream f -void Application::Messages::getErrors(std::ostream& f) -{ - int i = static_cast(errorMessage.size()); - if (i == 0) { - return; - } - f << endl << endl; - f << "************************************************" << endl; - f << " Cantera Error! " << endl; - f << "************************************************" << endl - << endl; - int j; - for (j = 0; j < i; j++) { - f << endl; - f << "Procedure: " << errorRoutine[j] << endl; - f << "Error: " << errorMessage[j] << endl; - } - f << endl << endl; - errorMessage.clear(); - errorRoutine.clear(); -} - -/* - * Print all of the error messages using function writelog. - * Write out all of the saved error messages to the log device. - * Cantera saves a stack of exceptions that it - * has caught in the Application class. This routine writes - * out all of the error messages to the log, usually stdout, - * and then clears them from internal storage. - * \ingroup errorhandling - */ -void showErrors() -{ - app()->logErrors() ; -} - -// Print out all of the error messages -void Application::Messages::logErrors() -{ - int i = static_cast(errorMessage.size()); - if (i == 0) { - return; - } - writelog("\n\n"); - writelog("************************************************\n"); - writelog(" Cantera Error! \n"); - writelog("************************************************\n\n"); - int j; - for (j = 0; j < i; j++) { - writelog("\n"); - writelog(string("Procedure: ")+ errorRoutine[j]+" \n"); - writelog(string("Error: ")+ errorMessage[j]+" \n"); - } - writelog("\n\n"); - errorMessage.clear(); - errorRoutine.clear(); -} - -/* - * Set an error condition in the application class without - * throwing an exception. - * This routine adds an error message to the end of the stack - * of errors that Cantera accumulates in the Application - * class. - * \ingroup errorhandling - */ -void setError(std::string r, std::string msg) -{ - app()->addError(r, msg) ; -} - -// Set an error condition in the application class without throwing an exception -void Application::Messages::addError(std::string r, std::string msg) -{ - errorMessage.push_back(msg); - errorRoutine.push_back(r); -} - - -//! Set the default directories for input files. -/*! - * %Cantera searches - * for input files along a path that includes platform-specific - * default locations, and possibly user-specified locations. This - * function installs the platform-specific directories on the - * search path. It is invoked at startup by appinit(), and never - * should need to be called by user programs. - * - * The current directory (".") is always searched first. Then, on - * Windows platforms, if environment variable COMMONPROGRAMFILES - * is set (which it should be on Win XP or Win 2000), then - * directories under this one will be added to the search - * path. The %Cantera Windows installer installs data files to this - * location. - * - * On the Mac, directory '/Applications/Cantera/data' is added to the - * search path. - * - * On any platform, if environment variable CANTERA_DATA is set to a - * directory name, then this directory is added to the search path. - * - * Finally, the location where the data files were installed when - * %Cantera was built is added to the search path. - * - * Additional directories may be added by calling function addDirectory. - * @ingroup inputfiles - */ -void Application::setDefaultDirectories() -{ - vector& dirs = inputDirs; - - // always look in the local directory first - dirs.push_back("."); - - -#ifdef _WIN32 - // Under Windows, the Cantera setup utility records the installation - // directory in the registry. Data files are stored in the 'data' and - // 'templates' subdirectories of the main installation directory. - - std::string installDir; - readStringRegistryKey("SOFTWARE\\Cantera\\Cantera 2.0", "InstallDir", installDir, ""); - if (installDir != "") { - dirs.push_back(installDir + "data"); - dirs.push_back(installDir + "templates"); - } -#endif - -#ifdef DARWIN - // - // add a default data location for Mac OS X - // - dirs.push_back("/Applications/Cantera/data"); -#endif - - // - // if environment variable CANTERA_DATA is defined, then add - // it to the search path - // - if (getenv("CANTERA_DATA") != 0) { - string datadir = string(getenv("CANTERA_DATA")); - dirs.push_back(datadir); - } - - // CANTERA_DATA is defined in file config.h. This file is written - // during the build process (unix), and points to the directory - // specified by the 'prefix' option to 'configure', or else to - // /usr/local/cantera. -#ifdef CANTERA_DATA - string datadir = string(CANTERA_DATA); - dirs.push_back(datadir); -#endif -} - - -// Add a directory to the input file search path. -// @ingroup inputfiles -void addDirectory(std::string dir) -{ - app()->addDataDirectory(dir) ; -} -void Application::addDataDirectory(std::string dir) -{ - DIR_LOCK() ; - if (inputDirs.size() == 0) { - setDefaultDirectories(); - } - string d = stripnonprint(dir); - size_t m, n = inputDirs.size(); - - // don't add if already present - for (m = 0; m < n; m++) { - if (d == inputDirs[m]) { - return; - } - } - - inputDirs.push_back(d); -} - -/* - * This routine will search for a file in the default - * locations specified for the application. - * See the routine setDefaultDirectories() listed above. - * - * The default set of directories specified for the application - * will be searched if a '/' or an '\\' is found in the - * name. If either is found then a relative path name is - * presumed, and the default directories are not searched. - * - * The presence of the file is determined by whether the file - * can be opened for reading by the current user. - * - * \return - * - * The absolute path name of the first matching - * file is returned. If a relative path name - * is indicated, the relative path name is returned. - * - * If the file is not found, a message is written to - * stdout and a CanteraError exception is thrown. - */ -std::string findInputFile(std::string name) -{ - return app()->findInputFile(name) ; -} - -std::string Application::findInputFile(std::string name) -{ - DIR_LOCK() ; - string::size_type islash = name.find('/'); - string::size_type ibslash = name.find('\\'); - string inname; - vector& dirs = inputDirs; - - int nd; - if (islash == string::npos && ibslash == string::npos) { - nd = static_cast(dirs.size()); - int i; - inname = ""; - for (i = 0; i < nd; i++) { - inname = dirs[i] + "/" + name; - ifstream fin(inname.c_str()); - if (fin) { - fin.close(); - return inname; - } - } - string msg; - msg = "\nInput file " + name - + " not found in director"; - msg += (nd == 1 ? "y " : "ies "); - for (i = 0; i < nd; i++) { - msg += "\n'" + dirs[i] + "'"; - if (i < nd-1) { - msg += ", "; - } - } - msg += "\n\n"; - msg += "To fix this problem, either:\n"; - msg += " a) move the missing files into the local directory;\n"; - msg += " b) define environment variable CANTERA_DATA to\n"; - msg += " point to the directory containing the file."; - throw CanteraError("findInputFile", msg); - } - - return name; -} - -doublereal toSI(std::string unit) -{ - doublereal f = Unit::units()->toSI(unit); - if (f) { - return f; - } else { - throw CanteraError("toSI","unknown unit string: "+unit); - } - return 1.0; -} - -doublereal actEnergyToSI(std::string unit) -{ - doublereal f = Unit::units()->actEnergyToSI(unit); - if (f) { - return f; - } - return 1.0; -} - -string canteraRoot() -{ - char* ctroot = 0; - ctroot = getenv("CANTERA_ROOT"); - if (ctroot != 0) { - return string(ctroot); - } -#ifdef CANTERA_ROOT - return string(CANTERA_ROOT); -#else - return ""; -#endif - -} - -// exceptions - -CanteraError::CanteraError(std::string proc, std::string msg) -{ - app()->addError(proc, msg); -} - -CanteraError::CanteraError() -{ -} - -CanteraError::~CanteraError() throw() -{ -} - - -ArraySizeError::ArraySizeError(std::string proc, size_t sz, size_t reqd) : - CanteraError(proc, "Array size ("+int2str(sz)+ - ") too small. Must be at least "+int2str(reqd)) {} - -ElementRangeError::ElementRangeError(std::string func, size_t m, size_t mmax) : - CanteraError(func, "Element index " + int2str(m) + - " outside valid range of 0 to " + int2str(mmax-1)) {} - - - -/////////////////////////////////////////////////////////// -// -// Warnings -// -////////////////////////////////////////////////////////// - -// Print a warning when a deprecated method is called. -// @param classnm Class the method belongs to -// @param oldnm Name of the deprecated method -// @param newnm Name of the method users should use instead -void deprecatedMethod(std::string classnm, std::string oldnm, std::string newnm) -{ - writelog(">>>> WARNING: method "+oldnm+" of class "+classnm - +" is deprecated.\n"); - writelog(" Use method "+newnm+" instead.\n"); - writelog(" (If you want to rescue this method from deprecated\n"); - writelog(" status, see http://www.cantera.org/deprecated.html)"); -} - - -void removeAtVersion(std::string func, std::string version) -{ - //if (version >= "CANTERA_VERSION") { - writelog("Removed procedure: "+func+"\n"); - writelog("Removed in version: "+version+"\n"); - throw CanteraError("removeAtVersion: "+ func,"procedure has been removed."); - // } -} - - -// defgroup logs Diagnostic Output (global.h) -// -// Writing diagnostic information to the screen or to a file. -// It is often useful to be able to write diagnostic messages to -// the screen or to a file. Cantera provides two sets of -// procedures for this purpose. The first set is designed to -// write text messages to the screen to document the progress of -// a complex calculation, such as a flame simulation.The second -// set writes nested lists in HTML format. This is useful to -// print debugging output for a complex calculation that calls -// many different procedures. - - -// defgroup textlogs Writing messages to the screen (global.h) -// ingroup logs -// -// Write a message to the screen. The string may be of any -// length, and may contain end-of-line characters. This method is -// used throughout Cantera to write log messages. It can also be -// called by user programs. The advantage of using writelog over -// writing directly to the standard output is that messages -// written with writelog will display correctly even when Cantera -// is used from MATLAB or other application that do not have a -// standard output stream. @ingroup textlogs - -// Write a message to the screen -void writelog(const std::string& msg) -{ - app()->writelog(msg); -} - -// Write a message to the screen -void Application::Messages::writelog(const std::string& msg) -{ - logwriter->write(msg); -} - -// Write a message to the screen. -void writelog(const char* msg) -{ - app()->writelog(msg); -} - -// Write a message to the screen -void Application::Messages::writelog(const char* pszmsg) -{ - logwriter->write(pszmsg) ; -} - -// Write an endl to the screen and flush output -void Application::Messages::writelogendl() -{ - logwriter->writeendl(); -} - -// Write a message to the screen using printf format -void writelogf(const char* fmt,...) -{ - enum { BUFSIZE = 2048 } ; - char sbuf[BUFSIZE] ; - - va_list args ; - - va_start(args, fmt) ; - -#ifdef _MSC_VER - _vsnprintf(sbuf, BUFSIZE, fmt, args) ; -#else - vsprintf(sbuf, fmt, args) ; -#endif - - writelog(sbuf) ; - - va_end(args) ; -} - -void writelogendl() -{ - app()->writelogendl(); -} - -// Write an error message and terminate execution. test. -// @ingroup textlogs -void error(const std::string& msg) -{ - app()->logerror(msg); -} - -// Write an error message and terminate execution -void Application::Messages::logerror(const std::string& msg) -{ - logwriter->error(msg) ; -} - -// @ingroup textlogs -int userInterface() -{ - return app()->getUserEnv(); -} - -// Return an integer specifying the application environment. -int Application::Messages::getUserEnv() -{ - return logwriter->env() ; -} - -// Install a logger. Called by the language interfaces to install an -// appropriate logger. -// @see Logger. -// @ingroup textlogs -void setLogger(Logger* logwriter) -{ - try { - app()->setLogger(logwriter) ; - } catch (std::bad_alloc) { - logwriter->error("bad alloc thrown by app()"); - } -} - -void Application::Messages::setLogger(Logger* _logwriter) -{ - if (logwriter == _logwriter) { - return ; - } - if (logwriter != 0) { - delete logwriter; - logwriter = 0 ; - } - logwriter = _logwriter; -} - - -#ifdef WITH_HTML_LOGS - -//////////////////////////////////////////////////////////////// -// -// defgroup HTML_logs Writing HTML Logfiles -// ingroup logs -// -// These functions are designed to allow writing HTML diagnostic -// messages in a manner that allows users to control how much -// diagnostic output to print. It works like this: Suppose you -// have function A that invokes function B that invokes function -// C. You want to be able to print diagnostic messages just from -// function A, or from A and B, or from A, B, and C, or to turn -// off printing diagnostic messages altogether. All you need to -// do is call 'beginLogGroup' within function A, and specify a -// loglevel value. Then in B, call beginLogGroup again, but -// without an explicit value for loglevel. By default, the -// current level is decremented by one in beginLogGroup. If it -// is <= 0, no log messages are written. Thus, if each function -// begins with beginLogGroup and calls endLogGroup before -// returning, then setting loglevel = 3 will cause messages from -// A, B, and C to be written (in nested HTML lists), loglevel = -// 2 results in messages only being written from A and B, etc. -// -///////////////////////////////////////////////////////////////// - - -// Create a new group for log messages. Usually this is called -// upon entering the function, with the title parameter equal to -// the name of the function or method. Subsequent messages -// written with addLogEntry will appear grouped under this -// heading, until endLogGroup() is called. -// @ingroup HTML_logs -void beginLogGroup(std::string title, int loglevel) -{ - app()->beginLogGroup(title, loglevel) ; -} - -void Application::Messages::beginLogGroup(std::string title, int _loglevel /*=-99*/) -{ - // loglevel is a member of the Messages class. - if (_loglevel != -99) { - loglevel = _loglevel; - } else { - loglevel--; - } - if (loglevel <= 0) { - return; - } - // Add the current loglevel to the vector of loglevels - loglevels.push_back(loglevel); - // Add the title of the current logLevel to the vector of titles - loggroups.push_back(title); - // If we haven't started an XML tree for the log file, do so here - if (xmllog == 0) { - // The top of this tree will have a zero pointer. - xmllog = new XML_Node("html"); - current = &xmllog->addChild("ul"); - } - // Add two children to the XML tree. - current = ¤t->addChild("li",""+title+""); - current = ¤t->addChild("ul"); -} - -// Add an entry to the log file. Entries appear in the form "tag: -// value". -// @ingroup HTML_logs -void addLogEntry(std::string tag, std::string value) -{ - app()->addLogEntry(tag, value) ; -} - -void Application::Messages::addLogEntry(std::string tag, std::string value) -{ - if (loglevel > 0 && current) { - current->addChild("li",tag+": "+value); - } -} - -// Add an entry to the log file. Entries appear in the form "tag: -// value". -// @ingroup HTML_logs -void addLogEntry(std::string tag, doublereal value) -{ - app()->addLogEntry(tag, value) ; -} - -void Application::Messages::addLogEntry(std::string tag, doublereal value) -{ - if (loglevel > 0 && current) { - current->addChild("li",tag+": "+fp2str(value)); - } -} - -// Add an entry to the log file. Entries appear in the form "tag: -// value". -// @ingroup HTML_logs -void addLogEntry(std::string tag, int value) -{ - app()->addLogEntry(tag, value) ; -} - -void Application::Messages::addLogEntry(std::string tag, int value) -{ - if (loglevel > 0 && current) { - current->addChild("li",tag+": "+int2str(value)); - } -} - -// Add an entry to the log file. -// @ingroup HTML_logs -void addLogEntry(std::string msg) -{ - app()->addLogEntry(msg) ; -} - -void Application::Messages::addLogEntry(std::string msg) -{ - if (loglevel > 0 && current) { - current->addChild("li",msg); - } -} - -// Close the current group of log messages. This is typically -// called just before leaving a function or method, to close the -// group of messages that were output from this -// function. Subsequent messages written with addLogEntry will -// appear at the next-higher level in the outline, unless -// beginLogGroup is called first to create a new group. -// @ingroup HTML_logs -void endLogGroup(std::string title) -{ - app()->endLogGroup(title) ; -} - -void Application::Messages::endLogGroup(std::string title) -{ - if (loglevel <= 0) { - return; - } - AssertThrowMsg(current, "Application::Messages::endLogGroup", - "Error while ending a LogGroup. This is probably due to an unmatched" - " beginnning and ending group"); - current = current->parent(); - AssertThrowMsg(current, "Application::Messages::endLogGroup", - "Error while ending a LogGroup. This is probably due to an unmatched" - " beginnning and ending group"); - current = current->parent(); - // Get the loglevel of the previous level and get rid of - // vector entry in loglevels. - loglevel = loglevels.back(); - loglevels.pop_back(); - if (title != "" && title != loggroups.back()) { - writelog("Logfile error." - "\n beginLogGroup: "+ loggroups.back()+ - "\n endLogGroup: "+title+"\n"); - write_logfile("logerror"); - } else if (loggroups.size() == 1) { - write_logfile(loggroups.back()+"_log"); - loggroups.clear(); - loglevels.clear(); - } else { - loggroups.pop_back(); - } -} - - -// Write the HTML log file. -/* - * Log entries are stored in memory in - * an XML tree until this function is called, which writes the - * tree to a file and clears the entries stored in memory. The - * output file will have the name specified in the 'file' - * argument. If this argument has no extension, the extension - * '.html' will be appended. Also, if the file already exists, an - * integer will be appended to the name so that no existing log - * file will be overwritten. - * WITH_HTML_LOGS must be defined. - * - * @param file Name of the file to be written - * @ingroup HTML_logs - */ -void write_logfile(std::string file) -{ - app()->write_logfile(file) ; -} - -// Write the HTML log file. -void Application::Messages::write_logfile(std::string file) -{ - if (!xmllog) { - return; - } - string::size_type idot = file.rfind('.'); - string ext = ""; - string nm = file; - if (idot != string::npos) { - ext = file.substr(idot, file.size()); - nm = file.substr(0,idot); - } else { - ext = ".html"; - nm = file; - } - - // see if file exists. If it does, find an integer that - // can be appended to the name to create the name of a file - // that does not exist. - string fname = nm + ext; - ifstream f(fname.c_str()); - if (f) { - int n = 0; - while (1 > 0) { - n++; - fname = nm + int2str(n) + ext; - ifstream f(fname.c_str()); - if (!f) { - break; - } - } - } - - // Now we have a file name that does not correspond to any - // existing file. Open it as an output stream, and dump the - // XML (HTML) tree to it. - - if (xmllog) { - ofstream f(fname.c_str()); - // go to the top of the tree, and write it all. - xmllog->root().write(f); - f.close(); - writelog("Log file " + fname + " written.\n"); - delete xmllog; - xmllog = 0; - current = 0; - } -} - -#endif // WITH_HTML_LOGS - -//=============================================================================================================== -//! split a string at a '#' sign. Used to separate a file name from an id string. -/*! - * @param src Original string to be split up. This is unchanged. - * @param file Output string representing the first part of the string, which is the filename. - * @param id Output string representing the last part of the string, which is the id. - */ -static void split_at_pound(const std::string& src, std::string& file, std::string& id) -{ - string::size_type ipound = src.find('#'); - if (ipound != string::npos) { - id = src.substr(ipound+1,src.size()); - file = src.substr(0,ipound); - } else { - id = ""; - file = src; - } -} -//=============================================================================================================== -/* - * This routine will locate an XML node in either the input - * XML tree or in another input file specified by the file - * part of the file_ID string. Searches are based on the - * ID attribute of the XML element only. - * - * param file_ID This is a concatenation of two strings separated - * by the "#" character. The string before the - * pound character is the file name of an xml - * file to carry out the search. The string after - * the # character is the ID attribute - * of the xml element to search for. - * The string is interpreted as a file string if - * no # character is in the string. - * - * param root If the file string is empty, searches for the - * xml element with matching ID attribute are - * carried out from this XML node. - */ -XML_Node* get_XML_Node(const std::string& file_ID, XML_Node* root) -{ - std::string fname, idstr; - XML_Node* db, *doc; - split_at_pound(file_ID, fname, idstr); - if (fname == "") { - if (!root) throw CanteraError("get_XML_Node", - "no file name given. file_ID = "+file_ID); - db = root->findID(idstr, 3); - } else { - doc = get_XML_File(fname); - if (!doc) throw CanteraError("get_XML_Node", - "get_XML_File failed trying to open "+fname); - db = doc->findID(idstr, 3); - } - if (!db) { - throw CanteraError("get_XML_Node", - "id tag '"+idstr+"' not found."); - } - return db; -} - - -/* - * This routine will locate an XML node in either the input - * XML tree or in another input file specified by the file - * part of the file_ID string. Searches are based on the - * XML element name and the ID attribute of the XML element. - * An exact match of both is usually required. However, the - * ID attribute may be set to "", in which case the first - * xml element with the correct element name will be returned. - * - * @param nameTarget This is the XML element name to look for. - * - * @param file_ID This is a concatenation of two strings separated - * by the "#" character. The string before the - * pound character is the file name of an xml - * file to carry out the search. The string after - * the # character is the ID attribute - * of the xml element to search for. - * The string is interpreted as a file string if - * no # character is in the string. - * - * @param root If the file string is empty, searches for the - * xml element with matching ID attribute are - * carried out from this XML node. - */ -XML_Node* get_XML_NameID(const std::string& nameTarget, - const std::string& file_ID, - XML_Node* root) -{ - string fname, idTarget; - XML_Node* db, *doc; - split_at_pound(file_ID, fname, idTarget); - if (fname == "") { - if (!root) { - return 0; - } - db = root->findNameID(nameTarget, idTarget); - } else { - doc = get_XML_File(fname); - if (!doc) { - return 0; - } - db = doc->findNameID(nameTarget, idTarget); - } - return db; -} - -//! Assigned storage for the static member of the FactoryBase class -std::vector FactoryBase::s_vFactoryRegistry; -} - -