Salome HOME
Copyright update 2022
[modules/paravis.git] / src / Plugins / TableReader / plugin / TableReaderModule / TableParser.cxx
1 // Copyright (C) 2010-2022  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "TableParser.h"
21
22 #include <QString>
23 #include <QStringList>
24
25 // STL includes
26 #include <fstream>
27 #include <sstream>
28 #include <stdexcept>
29
30 using namespace std;
31
32 int getLine(std::ifstream& streamIn, QString& str)
33 {
34   char tmp;
35   std::ostringstream streamOut;
36
37   while (streamIn.get(tmp))
38   {
39     streamOut << tmp;
40     if (tmp == '\n')
41       break;
42   }
43
44   streamOut << std::ends;
45   str = streamOut.str().c_str();
46
47   return !streamIn.eof();
48 }
49
50 bool Table2D::Check()
51 {
52   if (myRows.empty())
53     return false;
54
55   int iEnd = myRows[0].myValues.size();
56   if (iEnd == 0)
57   {
58     return false;
59   }
60
61   if (myColumnTitles.size() != iEnd)
62   {
63     myColumnTitles.resize(iEnd);
64   }
65
66   if (myColumnUnits.size() != iEnd)
67   {
68     myColumnUnits.resize(iEnd);
69   }
70
71   int jEnd = myRows.size();
72   for (int j = 0; j < jEnd; j++)
73   {
74     if (myRows[j].myValues.size() != iEnd)
75     {
76       return false;
77     }
78   }
79
80   return true;
81 }
82
83 std::vector<std::string> GetTableNames(
84   const char* fname, const char* separator, const bool firstStringAsTitles)
85 {
86   Table2D table;
87   std::vector<std::string> tableTitles;
88
89   int nb = 0;
90   table = GetTable(fname, separator, nb, firstStringAsTitles);
91   while (table.Check())
92   {
93     tableTitles.push_back(table.myTitle);
94     table = GetTable(fname, separator, ++nb, firstStringAsTitles);
95   }
96
97   return tableTitles;
98 }
99
100 Table2D GetTable(
101   const char* fname, const char* separator, const int tableNb, const bool firstStringAsTitles)
102 {
103   std::ifstream streamIn(fname);
104
105   if (!streamIn.good())
106   {
107     throw std::runtime_error("Unable to open input Post-Pro table file.");
108   }
109
110   QString tmp;
111   int count = 0;
112   do
113   {
114     // Find beginning of table (tables are separated by empty lines)
115     while (getLine(streamIn, tmp) && tmp.trimmed() == "")
116       ;
117
118     Table2D table2D;
119
120     bool isFirst = true;
121     while (!streamIn.eof() && tmp.trimmed() != "")
122     {
123       QString data = tmp.trimmed();
124       QString cmt = "";
125       QString keyword = "";
126
127       // Split string to data and comment (comment starts from '#' symbol)
128       int index = tmp.indexOf("#");
129       if (index >= 0)
130       {
131         data = tmp.left(index).trimmed();
132         cmt = tmp.mid(index + 1).trimmed();
133       }
134
135       // If comment is not empty, try to get keyword from it (separated by ':' symbol)
136       if (!cmt.isEmpty())
137       {
138         int index1 = cmt.indexOf(":");
139
140         if (index1 >= 0)
141         {
142           QString tmpstr = cmt.left(index1).trimmed();
143           if (tmpstr == QString("TITLE") || tmpstr == QString("COLUMN_TITLES") ||
144             tmpstr == QString("COLUMN_UNITS") || tmpstr == QString("COMMENT"))
145           {
146             keyword = tmpstr;
147             cmt = cmt.mid(index1 + 1).trimmed();
148           }
149         }
150       }
151
152       // If data is empty, process only comment
153       if (data.isEmpty())
154       {
155         // If keyword is found, try to process it
156         // elsewise it is a simple comment, just ignore it
157         if (!keyword.isEmpty())
158         {
159           if (keyword == QString("TITLE"))
160           {
161             QString title = cmt;
162             if (table2D.myTitle != "")
163             {
164               title = QString(table2D.myTitle.c_str()) + QString(" ") + title;
165             }
166             table2D.myTitle = title.toLatin1().constData();
167           }
168           else if (keyword == QString("COLUMN_TITLES"))
169           {
170             // Comment may contain column headers
171             QStringList strList = cmt.split("|", QString::SkipEmptyParts);
172
173             for (int i = 0; i < strList.count(); i++)
174             {
175               QString tmpstr = strList[i].trimmed();
176               table2D.myColumnTitles.push_back(tmpstr.toLatin1().constData());
177             }
178           }
179           else if (keyword == QString("COLUMN_UNITS"))
180           {
181             // Comment may contain column units
182             QStringList strList = cmt.split(" ", QString::SkipEmptyParts);
183
184             for (int i = 0; i < strList.count(); i++)
185             {
186               QString tmpstr = strList[i].trimmed();
187               table2D.myColumnUnits.push_back(tmpstr.toLatin1().constData());
188             }
189           }
190           else if (keyword == QString("COMMENT"))
191           {
192             // Keyword 'COMMENT' processing can be here,
193             // currently it is ignored
194           }
195         }
196         else
197         {
198           // Simple comment processing can be here,
199           // currently it is ignored
200         }
201       }
202       // If data is not empty, try to process it
203       else
204       {
205         Table2D::Row row;
206
207         QString datar1 = data.replace(QRegExp("\t"), " ");
208         QStringList valList = datar1.split(separator, QString::SkipEmptyParts);
209         if (table2D.myColumnTitles.size() == 0 && isFirst && firstStringAsTitles)
210         {
211           for (int i = 0; i < valList.count(); i++)
212           {
213             QString tmpstr = valList[i].trimmed();
214             table2D.myColumnTitles.push_back(tmpstr.toLatin1().constData());
215           }
216         }
217         else
218         {
219           if (!cmt.isEmpty())
220           {
221             row.myTitle = cmt.toLatin1().constData();
222           }
223
224           for (int i = 0; i < valList.count(); i++)
225           {
226             if (valList[i].trimmed() != "")
227             {
228               Table2D::Value val = valList[i].trimmed().toLatin1().constData();
229               row.myValues.push_back(val);
230             }
231           }
232
233           if (row.myValues.size() > 0)
234           {
235             table2D.myRows.push_back(row);
236           }
237         }
238
239         isFirst = false;
240       }
241       getLine(streamIn, tmp);
242     }
243
244     if (table2D.Check())
245     {
246       if (count == tableNb)
247       {
248         if (QString::fromStdString(table2D.myTitle).isEmpty())
249         {
250           table2D.myTitle = QString("Table:%1").arg(tableNb).toStdString();
251         }
252         return table2D;
253       }
254       count++;
255     }
256
257   } while (!streamIn.eof());
258
259   streamIn.close();
260
261   // Return empty table
262   Table2D emptyTable;
263   return emptyTable;
264 }