Salome HOME
Copyright update 2022
[modules/paravis.git] / src / Plugins / JSONReader / plugin / JSONReaderModule / vtkJSONReader.cxx
1 // Copyright (C) 2015-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 // Author: Roman NIKOLAEV
20
21 #include "vtkJSONReader.h"
22
23 #include <vtkObjectFactory.h>
24 #include <vtkTable.h>
25 #include <vtkInformationVector.h>
26 #include <vtkInformation.h>
27 #include <vtkStreamingDemandDrivenPipeline.h>
28 #include <vtkDoubleArray.h>
29 #include <vtkVariantArray.h>
30 #include <vtkStringArray.h>
31 #include <vtkStringToNumeric.h>
32 #include <vtkSetGet.h>
33 #include <vtksys/RegularExpression.hxx>
34
35 #include <stdexcept>
36 #include <sstream>
37 #include <fstream>
38 // Key words
39 #define MD  "_metadata"
40 #define CMT "_comment"
41 #define TBN "table_name"
42 #define TBD "table_description"
43 #define SHT "short_names"
44 #define LNG "long_names"
45 #define UNT "units"
46 #define DT "date"
47
48 #define NA "n/a"
49
50 // Exception
51 //---------------------------------------------------
52 vtkJSONException::vtkJSONException(const char *reason) : Reason(reason) {
53
54 }
55
56 //---------------------------------------------------
57 vtkJSONException::~vtkJSONException() noexcept {
58 }
59
60 //---------------------------------------------------
61 const char* vtkJSONException::what() const noexcept {
62   return Reason.c_str();
63 }
64
65
66 //---------------------------------------------------
67 class Container {
68 public:
69   typedef std::vector<std::pair<std::string,std::vector<double> > > DataType;
70   Container(){}
71   void initKeys(std::vector<std::string> &keys, std::string &err) {
72     for(int i = 0; i < (int)keys.size(); i++) {
73       if( !checkVar(keys[i].c_str()) ) {
74         std::ostringstream oss;
75         oss<<"Bad key value '"<<keys[i].c_str()<<"'";
76         err = oss.str();
77         return;
78       }
79       _data.push_back(std::make_pair(keys[i],std::vector<double>()));
80     }
81   }
82
83   void addValue(std::string key, double value, std::string& err) {
84     if( !checkVar(key.c_str()) ) {
85       std::ostringstream oss;
86       oss<<"Bad key value '"<<key.c_str()<<"'";
87       err = oss.str();
88       return;
89     }
90     bool found = false;
91     for(int i = 0; i < (int)_data.size(); i++) {
92       if(_data[i].first == key) {
93         _data[i].second.push_back(value);
94         found = true;
95         break;
96       }
97     }
98     if(!found) {
99       std::ostringstream oss;
100       oss<<"Bad key value '"<<key<<"'";
101       err = oss.str();
102     }
103   }
104   
105   DataType& getData() {
106     return _data;
107   }
108 private:
109   bool checkVar(const char* var) {
110     vtksys::RegularExpression regEx("^[a-zA-Z_][a-zA-Z0-9_]*$");
111     return regEx.find(var);
112   }
113 private:
114   DataType _data;
115 };
116
117 vtkStandardNewMacro(vtkJSONReader)
118
119 //---------------------------------------------------
120 vtkJSONReader::vtkJSONReader() {
121   this->SetNumberOfInputPorts(0);
122   this->SetNumberOfOutputPorts(1);
123   this->FileName = NULL;
124 }
125
126 //---------------------------------------------------
127 vtkJSONReader::~vtkJSONReader()
128 {
129   this->SetFileName(NULL);
130 }
131
132 //---------------------------------------------------
133 void vtkJSONReader::PrintSelf(ostream& os, vtkIndent indent)
134 {
135   this->Superclass::PrintSelf(os, indent);
136   os << indent << "FileName: " 
137       << (this->FileName ? this->FileName : "(none)") << endl;
138 }
139
140 //---------------------------------------------------
141 int vtkJSONReader::RequestData(vtkInformation*, 
142                                vtkInformationVector**,
143                                vtkInformationVector* outputVector) {
144   vtkTable* const output_table = vtkTable::GetData(outputVector);
145   
146   vtkInformation* const outInfo = outputVector->GetInformationObject(0);
147   if(outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) &&
148      outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) > 0) {
149     return 0;
150   }
151   
152   Json::Value root;
153   int parseResult = 0;
154   parseResult = this->CanParseFile(this->FileName, root);
155   if(!parseResult)
156     return 0;
157
158   try {
159     this->Parse(root, output_table);
160     return 1;
161   } 
162   catch(vtkJSONException& e)  {
163     std::ostringstream oss;
164     oss<<e.what();
165     if(this->HasObserver("ErrorEvent") )
166       {
167         this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
168       }
169     else
170       {
171         vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str())); 
172         vtkObject::BreakOnError();
173       }
174     return 0;
175   }
176 }
177
178 //---------------------------------------------------
179 int vtkJSONReader::CanParseFile(const char *fname, Json::Value &root)
180 {
181   if ( !fname) {
182     vtkErrorMacro(<< "Input file name is not specified !!! ");
183     return 0;
184   }
185
186   std::ifstream file;
187   std::ostringstream oss;
188   bool parsedSuccess = true;
189   Json::Reader reader;
190
191   file.open(fname);
192   if ( !file.is_open() ) {
193       oss<< "Unable to open file: " << this->FileName;
194       parsedSuccess = false;
195   } else {
196     parsedSuccess = reader.parse(file, root, false);
197     file.close();    
198   }
199   if ( !parsedSuccess ) {
200     if(oss.str().empty()) {
201       oss<<"Failed to parse JSON file: " << "\n" << reader.getFormattedErrorMessages();
202     }
203     if(this->HasObserver("ErrorEvent") ) {
204       this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
205     }
206     else {
207       vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str())); 
208       vtkObject::BreakOnError();
209     }
210     return 0;
211   }
212   return 1;
213 }
214
215 //---------------------------------------------------
216 void vtkJSONReader::Parse(Json::Value& root, vtkTable *table) {
217   bool hasShortNames = false;
218   bool hasUnits = false;
219   Container container;
220
221
222   Json::Value jSONListOfNames;
223   Json::Value jSONListOfUnits;
224   Json::Value jSONTableName;
225   std::vector<std::string> short_names;
226
227   if ( root.isMember(MD) ) {
228     Json::Value jSONMetaData = root.get(MD, Json::Value::null);
229
230     if ( jSONMetaData.isMember(CMT) ) {
231       Json::Value jSONComment = jSONMetaData.get(CMT, Json::Value::null);
232       vtkDebugMacro(<<"Table Comment  : " << jSONComment.asString());
233     }
234     if ( jSONMetaData.isMember(TBN) ) {
235       jSONTableName = jSONMetaData.get(TBN, Json::Value::null);      
236       vtkDebugMacro(<<"Table Name  : " << jSONTableName.asString());
237     }
238
239     if ( jSONMetaData.isMember(TBD) ) {
240       Json::Value jSONTableDescription = jSONMetaData.get(TBD, Json::Value::null);
241       vtkDebugMacro(<<"Table Description  : "  << jSONTableDescription.asString());
242     }
243
244     if ( jSONMetaData.isMember(DT) ) {
245       Json::Value jSONDate = jSONMetaData.get("date", Json::Value::null);
246       vtkDebugMacro(<<"Date  : " << jSONDate.asString());
247     }
248
249     if ( jSONMetaData.isMember(SHT) ) {
250       hasShortNames = true;
251       jSONListOfNames = jSONMetaData.get(SHT, Json::Value::null);
252       std::ostringstream oss;
253       oss<< "Short Names : [ ";
254       for (int i = 0; i < (int)jSONListOfNames.size(); i++) {
255         oss << "'" << jSONListOfNames[i].asString() << "'";
256         short_names.push_back(jSONListOfNames[i].asString());
257         if ( i != (int)jSONListOfNames.size() - 1 ) {
258           oss << ", ";
259         }
260       }
261       oss << " ]";
262       vtkDebugMacro(<<oss.str().c_str());
263     }
264
265     Json::Value jSONListOfLongName;
266     if ( jSONMetaData.isMember(LNG) ) {
267       jSONListOfLongName = jSONMetaData.get(LNG, Json::Value::null);
268       std::ostringstream oss;
269       oss << "Long Names : [ ";
270       for (int i = 0; i < (int)jSONListOfLongName.size(); ++i) {
271         oss << "'" << jSONListOfLongName[i].asString() << "'";
272         if ( i != (int)jSONListOfLongName.size()-1 ) {
273           oss << ", ";
274         }
275       }
276       oss<<" ]";
277       vtkDebugMacro(<<oss.str().c_str());
278     }
279     if ( jSONMetaData.isMember(UNT) ) {
280       jSONListOfUnits = jSONMetaData.get(UNT, Json::Value::null);
281       hasUnits = true;
282       std::ostringstream oss;
283       oss << "Units : [ ";
284       for (int i = 0; i < (int)jSONListOfUnits.size(); ++i) {
285         oss << "'" << jSONListOfUnits[i].asString() << "'";
286         if ( i != (int)jSONListOfUnits.size()-1 ) {
287           oss << ", ";
288         }
289       }
290       oss << " ]";
291       vtkDebugMacro(<<oss.str().c_str());
292     }
293     root.removeMember(MD);
294   }
295
296   Json::Value::iterator it = root.begin();
297   Json::Value newRoot = Json::Value::null;
298   
299   int nbElems=0;
300   bool hasArray = false;
301   for( ; it != root.end(); it++) {
302     nbElems++;
303     if((*it).type() == Json::arrayValue) {
304       newRoot = (*it);
305       hasArray = true;
306     }
307   }
308   if(hasArray && nbElems > 1) {
309     throw vtkJSONException("Wrong JSON file: it contains array and others elements");
310   }
311
312   if(newRoot == Json::Value::null) {
313     newRoot = root;
314   }
315     
316   it = newRoot.begin();
317   bool initContainer = false;
318   for( ; it != newRoot.end(); it++) {
319     if((*it).type() != Json::objectValue) {
320       std::ostringstream oss;
321       oss<<"Wrong JSON file: unexpected element, named '"<<(it.name())<<"'";
322       throw vtkJSONException(oss.str().c_str());
323     }
324     Json::Value::Members members = (*it).getMemberNames();    
325     if(!initContainer) {
326       if(!hasShortNames) {
327         short_names = members;
328       }
329       std::string err;
330       container.initKeys(short_names,err);
331       if(!err.empty()){
332         throw vtkJSONException(err.c_str());
333       }
334       initContainer = true;
335     }
336     for(int i=0; i < (int)members.size(); i++) { 
337       Json::Value val = (*it).get(members[i],Json::Value::null);
338       double value = 0.0;
339       switch (val.type()) {     
340           case Json::stringValue: {
341             std::string s("Item with name '");
342             s += it.name();
343             s += "' has key '";
344             s += members[i];
345             s += "' with string value '";
346             s += val.asString();
347             s += "' ! Value set to 0.0";
348             if(this->HasObserver("WarningEvent") ) {
349               this->InvokeEvent("WarningEvent",const_cast<char *>(s.c_str()));
350             }
351             else {
352               vtkOutputWindowDisplayWarningText(const_cast<char *>(s.c_str())); 
353             }
354             break;
355           }
356           default:
357             value = val.asDouble();
358       }
359       std::string err;
360       container.addValue(members[i],value,err);
361       if(!err.empty()){
362         throw vtkJSONException(err.c_str());
363       }
364     }
365   }
366   
367   table->GetInformation()->Set(vtkDataObject::FIELD_NAME(), jSONTableName.asString().c_str());
368   Container::DataType data = container.getData();
369   if(hasUnits && data.size() != jSONListOfUnits.size()) {
370     throw vtkJSONException("W");
371   }
372   
373   int nbRows = 0;
374   if(data.size() > 0)
375     nbRows = data[0].second.size();
376
377   for(int i = 0; i < (int)data.size(); i++) {
378     vtkDoubleArray* newCol = vtkDoubleArray::New();
379     newCol->SetNumberOfValues(nbRows);
380     std::string name = data[i].first;
381     name += "[";
382     if(!jSONListOfUnits[i].asString().empty()){
383       name += jSONListOfUnits[i].asString();
384     } else {
385       name += NA;
386     }
387     name += "]";
388     newCol->SetName(name.c_str());
389     for(int j = 0; j < (int)data[i].second.size(); j++) {
390       newCol->SetValue(j, data[i].second[j]);
391     }
392     table->AddColumn(newCol);
393   }
394 }