fileparser.h

00001 /*************************************************************************************
00002  * MechSys - A C++ library to simulate (Continuum) Mechanical Systems                *
00003  * Copyright (C) 2005 Dorival de Moraes Pedroso <dorival.pedroso at gmail.com>       *
00004  * Copyright (C) 2005 Raul Dario Durand Farfan  <raul.durand at gmail.com>           *
00005  *                                                                                   *
00006  * This file is part of MechSys.                                                     *
00007  *                                                                                   *
00008  * MechSys is free software; you can redistribute it and/or modify it under the      *
00009  * terms of the GNU General Public License as published by the Free Software         *
00010  * Foundation; either version 2 of the License, or (at your option) any later        *
00011  * version.                                                                          *
00012  *                                                                                   *
00013  * MechSys is distributed in the hope that it will be useful, but WITHOUT ANY        *
00014  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A   *
00015  * PARTICULAR PURPOSE. See the GNU General Public License for more details.          *
00016  *                                                                                   *
00017  * You should have received a copy of the GNU General Public License along with      *
00018  * MechSys; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, *
00019  * Fifth Floor, Boston, MA 02110-1301, USA                                           *
00020  *************************************************************************************/
00021 
00022 #ifndef MECHSYS_FILEPARSER_H
00023 #define MECHSYS_FILEPARSER_H
00024 
00025 #include <fstream>
00026 #include <sstream>
00027 #include <algorithm>
00028 #include <map>
00029 
00030 #ifdef HAVE_CONFIG_H
00031   #include "config.h"
00032 #else
00033   #ifndef REAL
00034     #define REAL double
00035   #endif
00036 #endif
00037 
00038 #include "util/array.h"
00039 #include "util/string.h"
00040 #include "util/lineparser.h"
00041 #include "util/exception.h"
00042 
00043 class FileParser
00044 {
00045 public:
00046     // Constructor
00047     FileParser(String const & Filename) : _comm_char('#'), _filename(Filename) { _open(_filename); }
00048     // Destructor
00049     ~FileParser() { _file.close(); }
00050     // Structures
00051     struct Tags
00052     {
00053         String L1;
00054         String L2;
00055         String Comment;
00056         bool   Required;
00057         String Default;
00058     };
00059     typedef std::map< String,Array<REAL> > Table;
00060     typedef std::map< String,String >      StructVals;
00061     // Methods
00062     int         CurrentLineNumber    ();
00063     String      GetCurrentLine       ();
00064     bool        TryGetCurrentLine    (String & line);
00065     void        Advance              ();
00066     void        JumpCommentsOrBlanks ();
00067     template<typename Type>
00068     void        ReadFirstWord        (Type & Val);
00069     bool        IsEOF                () { return _is_EOF; }
00070     void        Reset                () { _file.close(); _open(_filename); }
00071     template<typename Type>
00072     void        ReadNextValue        (Type & Val) { JumpCommentsOrBlanks(); ReadFirstWord(Val); Advance(); }
00073     template<typename Type>
00074     void        FindKeyAndFillArray  (String const & StrKey, Array<Type> & A);
00075     void        SetCommChar          (char CommChar) { _comm_char = CommChar; }
00076     void        StructFile           (Tags const * tags, int num_tags, StructVals & values);
00077     void        ReadTable            (Table & t);
00078     // Static
00079     static bool CheckForFile(String const & Filename) // {{{
00080     {
00081         // returns true if file exists and false otherwise
00082         std::ifstream file(Filename.GetSTL().c_str(),std::ios::in);
00083         if     (file.fail()) { return false; }
00084         else { file.close();   return true;  }
00085     } // }}}
00086     static String GetBackupFilename(String const & originalFN) // {{{
00087     {
00088         // ex.:
00089         //         originalFN                    |   newFN
00090         //        -------------------------------------------
00091         //        name.txt                       |  name.txt~
00092         //        name.txt~                      |  name.txt~
00093         //        name.txt<file does not exist>  |  name.txt
00094         //
00095         // OBS.: return originalFN if file does not exist
00096         
00097         // Check if file exists
00098         if (FileParser::CheckForFile(originalFN)) // file exists
00099         {
00100             if ( originalFN[ originalFN.size()-1 ] == '~' ) // already has a tilde at the end
00101                 return originalFN;
00102             else
00103             {
00104                 String tmp_fn = originalFN;
00105                 tmp_fn.append(_T("~"));
00106                 return tmp_fn;
00107             }
00108         }
00109         else
00110             return originalFN;
00111     } // }}}
00112 private:
00113     // Data
00114     char          _comm_char; // Comments char, ex.: '#'
00115     bool          _is_EOF;
00116     String        _filename;
00117     std::ifstream _file;
00118     String        _str_current_line;
00119     int           _current_line_num;
00120     // Functions
00121     void _open(String const & Filename);
00122 }; // 
00123 
00124 
00126 
00127 
00128 inline int FileParser::CurrentLineNumber() // {{{
00129 {
00130     if (_is_EOF)
00131         throw new Fatal(_("FileParser::GetCurrentLine: Can not return current line number because EOF. Filename = < %s >"), _filename.c_str());
00132     return _current_line_num; 
00133 } // }}}
00134 
00135 inline String FileParser::GetCurrentLine() // {{{
00136 {
00137     if (_is_EOF)
00138         throw new Fatal(_("FileParser::GetCurrentLine: Can not return current line number because EOF. Filename = < %s >"), _filename.c_str());
00139     return _str_current_line; 
00140 } // }}}
00141 
00142 inline bool FileParser::TryGetCurrentLine(String & line) // {{{
00143 {
00144     if (_is_EOF) return false;
00145     else
00146     {
00147         line = _str_current_line;
00148         return true;
00149     }
00150 } // }}}
00151 
00152 inline void FileParser::Advance() // {{{
00153 {
00154     // Check EOF
00155     if (_is_EOF) 
00156         throw new Fatal(_("FileParser::Advance: Can not advance because EOF"));
00157 
00158     // If not is EOF read line
00159     std::string buf;
00160     if (!std::getline(_file, buf)) _is_EOF = true;
00161     else
00162     {
00163         _str_current_line = buf;
00164         _current_line_num++;
00165     }
00166 } // }}}
00167 
00168 inline void FileParser::JumpCommentsOrBlanks() // {{{
00169 {
00170     do
00171     {
00172         std::istringstream iss(_str_current_line.GetSTL());
00173         String word;
00174         if (iss>>word) // Comments or data
00175         {
00176             if (word[0]==_comm_char)
00177             {
00178                 if (!TryGetCurrentLine(_str_current_line)) break;
00179                 Advance();
00180             }
00181             else
00182                 break;
00183         }
00184         else // Blank lines (?)
00185         {
00186             if (!TryGetCurrentLine(_str_current_line)) break;
00187             Advance();
00188         }
00189     } while (true);
00190 } // }}}
00191 
00192 template<typename Type>
00193 inline void FileParser::ReadFirstWord(Type & Val) // {{{
00194 {
00195     std::istringstream iss(_str_current_line.GetSTL());
00196     if (!(iss >> Val))
00197         throw new Fatal(_("FileParser::ReadFirstWord: Invalid data value. _current_line_num = %d"), _current_line_num);
00198 } // }}}
00199 
00200 inline void FileParser::_open(String const & Filename) // {{{
00201 {
00202     // EOF
00203     _is_EOF = false;
00204 
00205     // Open file
00206     _file.open(Filename.GetSTL().c_str(), std::ios::in);
00207     if (_file.fail())
00208         throw new Warning(_("FileParser::Constructor: Could not open file < %s >"), Filename.c_str());
00209     
00210     // Read current (first) line
00211     std::string buf;
00212     if (!std::getline(_file, buf))
00213         throw new Warning(_("FileParser::Constructor: Could not read first line of file < %s >"), Filename.c_str());
00214     _str_current_line = buf;
00215     _current_line_num = 1;
00216 } // }}}
00217 
00218 template<typename Type>
00219 inline void FileParser::FindKeyAndFillArray(String const & StrKey, Array<Type> & A) // {{{
00220 {
00221     /* filename.example // {{{
00222     ################################################ comments
00223 
00224     WrongKey 1.1 2.2 3.3 4.4 5.5 6.6
00225     
00226     #-------------------------------------------
00227 
00228     StrKey 1.1 2.2 3.3 4.4 5.5 6.6
00229 
00230     4 123 1234 
00231 
00232     }}} */
00233 
00234     // Find StrKey
00235     bool is_key_found = false;
00236     do
00237     {
00238         // Check EOF
00239         if (_is_EOF) 
00240             throw new Fatal(_("FileParser::FindKeyAndFillArray: Could not find StrKey = < %s > inside file < %s >"), StrKey.c_str(), _filename.c_str());
00241 
00242         // Find str_key == ModelName
00243         JumpCommentsOrBlanks();
00244         LineParser LP(GetCurrentLine());
00245         String str_key;
00246         LP >> str_key;
00247         if (str_key==StrKey)
00248         {
00249             Type value;
00250             while (LP>>value)
00251             {
00252                 A.push_back(value);
00253             }
00254             is_key_found = true;
00255         }
00256         else
00257             Advance();
00258     } while (!is_key_found);
00259 } // }}}
00260 
00261 inline void FileParser::StructFile(Tags const * tags, int num_tags, StructVals & values) // {{{
00262 {
00263     // Check if file wasn't read
00264     if (IsEOF()) throw new Fatal(_("FileParser::StructFile: Could not read file < %s >. Probably file was already read."), _filename.c_str());
00265 
00266     // Initialize values
00267     values.clear();
00268     for (int i=0; i<num_tags; ++i)
00269     {
00270         String fullkey(tags[i].L1); fullkey.append(_T(".")); fullkey.append(tags[i].L2);
00271         values[fullkey] = "";
00272     }
00273     
00274     // Read file
00275     while (!IsEOF())
00276     {
00277         JumpCommentsOrBlanks();
00278         String line;
00279         if (!TryGetCurrentLine(line)) break;
00280         LineParser LP(line);
00281         Advance();
00282         String key;    LP>>key;
00283         String subkey; LP>>subkey;
00284         String fullkey(key); fullkey.append(_T(".")); fullkey.append(subkey);
00285         bool found = false;
00286         for (int i=0; i<num_tags; ++i)
00287         {
00288             if (key==tags[i].L1 && subkey==tags[i].L2)
00289             {
00290                 String val;
00291                 while (LP>>val)
00292                 {
00293                     if (val[0]==_comm_char) break;
00294                     else
00295                     {
00296                         if (values[fullkey].size()>0) values[fullkey].append(_T(" "));
00297                         values[fullkey].append(val);
00298                     }
00299                 }
00300                 found = true;
00301                 break;
00302             }
00303         }
00304         if (!found)
00305             throw new Fatal(_("FileParser::StructFile: File < %s >: Pair < %s %s > is not valid."), _filename.c_str(), key.c_str(), subkey.c_str());
00306     }
00307 
00308     // Additional check and configuration
00309     for (int i=0; i<num_tags; ++i)
00310     {
00311         String fullkey(tags[i].L1); fullkey.append(_T(".")); fullkey.append(tags[i].L2);
00312 
00313         // Check required values
00314         if (tags[i].Required && values[fullkey]=="")
00315             throw new Fatal(_("FileParser::StructFile: The pair < %s %s > must be provided by file < %s >"), tags[i].L1.c_str(), tags[i].L2.c_str(), _filename.c_str());
00316 
00317         // Set default values
00318         if (!tags[i].Required && tags[i].Default!=_T("") && values[fullkey]==_T(""))
00319             values[fullkey] = tags[i].Default;
00320     }
00321     
00322 } // }}}
00323 
00324 inline void FileParser::ReadTable(Table & T) // {{{
00325 {
00326     // Check if file wasn't read
00327     if (IsEOF()) throw new Fatal(_("FileParser::ReadTable: Could not read file < %s >. Probably file was already read."), _filename.c_str());
00328 
00329     // Read Header ==> num of columns
00330     Array<String> header;
00331     LineParser LP(GetCurrentLine());
00332     String key;
00333     while (LP>>key) header.push_back(key);
00334     if (header.size()<1) throw new Fatal(_("FileParser::ReadTable: File format invalid < %s >. Header must have at least one tag (one column)"), _filename.c_str());
00335 
00336     // Prepare table (map)
00337     T.clear(); // clear current values
00338     Array<REAL> a_null;
00339     for (size_t i=0; i<header.size(); ++i)
00340         T[header[i]] = a_null;
00341 
00342     // Read values
00343     size_t num_lines=0;
00344     while (!IsEOF())
00345     {
00346         // read a line and distribute the values to all columns
00347         Advance();
00348         String line;
00349         if (!TryGetCurrentLine(line)) break;
00350         LP.Reset(line);
00351         size_t i_col=0; REAL col_val;
00352         while (LP>>col_val)
00353         {
00354             if (i_col>header.size()-1)
00355                 throw new Fatal(_("FileParser::ReadTable: File < %s >: Too many values: Number of columns (%d) must be equal to the number (%d) of header tags"), _filename.c_str(), i_col+1, header.size());
00356             T[header[i_col]].push_back(col_val);
00357             i_col++;
00358         }
00359         if (i_col!=header.size())
00360             throw new Fatal(_("FileParser::ReadTable: File < %s >: Not enough values: Number of columns (%d) must be equal to the number (%d) of header tags"), _filename.c_str(), i_col, header.size());
00361         num_lines++;
00362     }
00363 
00364     // Check-up
00365     Table::const_iterator it = T.begin();
00366     while (it!=T.end())
00367     {
00368         if (it->second.size()!=num_lines)
00369             throw new Fatal(_("FileParser::ReadTable:  INTERNAL ERROR:  There is an inconsistency with the number of lines of each column (%d != %d)"), it->second.size(), num_lines);
00370         it++;
00371     }
00372     
00373 } // }}}
00374 
00375 #endif //MECHSYS_FILEPARSER
00376 
00377 // vim:fdm=marker

Generated on Wed Jan 24 15:56:27 2007 for MechSys by  doxygen 1.4.7