X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FPlugins%2FJSONReader%2FJSONParser%2FvtkJSONParser.cxx;fp=src%2FPlugins%2FJSONReader%2FJSONParser%2FvtkJSONParser.cxx;h=cfbcbdbdbd9aa2d2f46205df6a1e8bb1919a42c7;hb=5565ef9400614283ee2f1c63c99a60463f893a78;hp=0000000000000000000000000000000000000000;hpb=d893209725724d9e7fdf5c0bb66cfdd4b57f4ebb;p=modules%2Fparavis.git diff --git a/src/Plugins/JSONReader/JSONParser/vtkJSONParser.cxx b/src/Plugins/JSONReader/JSONParser/vtkJSONParser.cxx new file mode 100644 index 00000000..cfbcbdbd --- /dev/null +++ b/src/Plugins/JSONReader/JSONParser/vtkJSONParser.cxx @@ -0,0 +1,807 @@ +// Copyright (C) 2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author: Roman NIKOLAEV + +#include "vtkJSONParser.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +//DEBUG macro +//#define __DEBUG + + +// Key worlds +#define MD "_metadata" +#define CMT "_comment" +#define TBN "table_name" +#define TBD "table_description" +#define SHT "short_names" +#define LNG "long_names" +#define UNT "units" +#define DT "date" + +// Separators +#define COMMA ',' + +#define COLON ':' + +#define OSB '[' +#define CSB ']' + +#define OCB '{' +#define CCB '}' +#define ENDL '\n' +#define QTS '"' +#define SPS ' ' +#define TAB '\t' + +#define NA "n/a" + + +#define addInfo(info) \ + Info I; \ + I.type = info; \ + I.ln = this->LineNumber; \ + I.cn = this->ColumnNumber; \ + I.pos = ftell(this->File); \ + this->CInfoVector.push_back(I); + +//--------------------------------------------------- +bool isBlankOrEnd(const char c) { + return c == SPS || c == ENDL || c == TAB || c == EOF; +} + +bool isDigitOrDot(const char c) { + return (c >= '.' && c <='9') || c == '+' || c == '-' || c == 'e'; +} + +// Exception +//--------------------------------------------------- +vtkJSONException::vtkJSONException(const char *reason) : Reason(reason) { +} + +//--------------------------------------------------- +vtkJSONException::~vtkJSONException() throw () { +} + +//--------------------------------------------------- +const char* vtkJSONException::what() const throw() { + return Reason.c_str(); +} + +// Containers to store information about nodes: + +// Base node +//--------------------------------------------------- +class vtkJSONNode { +public: + vtkJSONNode() { Name = 0; } + virtual ~vtkJSONNode() { } + const char* GetName() { return this->Name ; } + void SetName(const char* name) { this->Name = name; } +private: + const char* Name; +}; + +class vtkJSONMetaNode : public vtkJSONNode { +public: + vtkJSONMetaNode() : vtkJSONNode() { + this->SetName(MD); + this->Comment = 0; + this->TableName = 0; + this->TableDescription = 0; + this->Date = 0; + } + + virtual ~vtkJSONMetaNode() { + delete this->Comment; this->Comment = 0; + delete this->TableName; this->TableName = 0; + delete this->Date; this->Date= 0; + delete this->TableDescription; this->TableDescription = 0; + for(int i = 0; i < this->ShortNames.size(); i++) { + delete this->ShortNames[i]; this->ShortNames[i] = 0; + } + for(int i = 0; i < this->LongNames.size(); i++) { + delete this->LongNames[i]; this->LongNames[i] = 0; + } + for(int i = 0; i < this->Units.size(); i++) { + delete this->Units[i]; this->Units[i] = 0; + } + } + + void SetComment(const char* comment) { this->Comment = comment; } + const char* GetComment() { return this->Comment; } + + void SetTableName(const char* name) { this->TableName = name; } + const char* GetTableName() { return this->TableName; } + + void SetDate(const char* name) { this->Date = name; } + const char* GetDate() { return this->Date; } + + void SetTableDescription(const char* description) { this->TableDescription = description; } + const char* GetDescription() { return this->TableDescription; } + + void SetShortNames(std::vector names) { this->ShortNames = names; } + std::vector GetShortNames() { return this->ShortNames; } + + void SetLongNames(std::vector names) { this->LongNames = names; } + std::vector GetLongNames() { return this->LongNames; } + + void SetUnits(std::vector units) { this->Units = units; } + std::vector GetUnits() { return this->Units; } + +private: + const char* Comment; + const char* TableName; + const char* TableDescription; + const char* Date; + std::vector ShortNames; + std::vector LongNames; + std::vector Units; +}; + +struct char_cmp { + bool operator () (const char *a, const char *b) const + { + return strcmp(a,b)<0; + } +}; + +typedef std::map vtkJSONMap; + +class vtkJSONInfoNode : public vtkJSONNode { +public: + vtkJSONInfoNode(const char* name) : vtkJSONNode() { + this->SetName(name); + } + virtual ~vtkJSONInfoNode() { + vtkJSONMap::iterator it = this->Values.begin(); + for( ;it != this->Values.end(); it++) { + delete it->first; + const char* c = it->first; + c = 0; + } + } + void AddValue(const char* key, const double value) { + this->Values.insert(std::pair(key,value)); + } + vtkJSONMap& GetValues() { return this->Values; } +private: + vtkJSONMap Values; +}; + + +//--------------------------------------------------- +vtkStandardNewMacro(vtkJSONParser); + +//--------------------------------------------------- +vtkJSONParser::vtkJSONParser() +{ + this->FileName = NULL; + this->LineNumber = 1; + this->ColumnNumber = 0; + this->File = 0; + this->InsideQuotes = false; + this->LastString = 0; + this->CurrentNode = 0; + this->ParseList = false; + this->ParseObjectList = false; + this->LastValue = NAN; + this->ShortNamesFilled = false; +} + +//--------------------------------------------------- +vtkJSONParser::~vtkJSONParser() +{ + delete this->FileName; + this->FileName = 0; +} + + +//--------------------------------------------------- +int vtkJSONParser::Parse(vtkTable* theTable) +{ + if(!this->FileName) { + vtkErrorMacro("The name of the file is not defined"); + return 1; + } + this->File = fopen( this->FileName, "r" ); + + if( !this->File ) { + std::string message = std::string("Can't open file: ") + std::string(this->FileName); + throwSimpleException(message.c_str()); + return 1; + } + char ch = 0; + + this->ExpectedCharacters.push_back(OCB); + + while( ch != EOF ) { + ch = fgetc(this->File); + processCharacter(ch); + if(isDigitsAllowed() && isDigitOrDot(ch)) { + readDoubleValue(); + } + int nb = 1; + switch(ch) { + case OCB: processOCB(); break; + case CCB: processCCB(); break; + + case OSB: processOSB(); break; + case CSB: processCSB(); break; + + case ENDL: processENDL(); nb = 0; break; + + case QTS: processQTS(); break; + + case COLON: processCOLON(); break; + + case COMMA: processCOMMA(); break; + } + this->ColumnNumber+=nb; + } + fclose(this->File); + + if(this->CInfoVector.size() > 0 ) { + throwException("braket is not closed ", + this->CInfoVector.back().ln, + this->CInfoVector.back().cn); + } + + finalize(theTable); + clean(); + return 1; +} + +//--------------------------------------------------- +void vtkJSONParser::finalize( vtkTable *t ) { + std::vector::iterator it = this->Nodes.begin(); + vtkJSONMetaNode* mn = 0; + for( ; it != this->Nodes.end(); it++ ) { + mn = dynamic_cast(*it); + if (mn) + break; + } + std::vector units; + if(mn) { + if(!mn->GetShortNames().empty()) { + this->ShortNames.clear(); + this->ShortNames = mn->GetShortNames(); + } + t->GetInformation()->Set(vtkDataObject::FIELD_NAME(), mn->GetTableName() ? mn->GetTableName() : ""); + units = mn->GetUnits(); + } + + long nbRow = mn ? (this->Nodes.size() - 1 ) : this->Nodes.size(); + for (long col=0; col < this->ShortNames.size(); col++) { + vtkDoubleArray* newCol = vtkDoubleArray::New(); + newCol->SetNumberOfValues(nbRow); + vtkJSONInfoNode* in = 0; + std::string name = this->ShortNames[col]; + name += "["; + if(col < units.size()){ + name += units[col]; + } else { + name += NA; + } + name += "]"; + newCol->SetName(name.c_str()); + it = this->Nodes.begin(); + long row = 0; + for( ; it != this->Nodes.end(); it++ ) { + in = dynamic_cast(*it); + if (in) { + vtkJSONMap& vl = in->GetValues(); + vtkJSONMap::iterator mit = vl.find(this->ShortNames[col]); + if(mit != vl.end()) { + newCol->SetValue(row, mit->second); + vl.erase(mit); + delete mit->first; + const char* c = mit->first; + c = 0; + row++; + } else { + std::string s("Item with name '"); + s+=in->GetName(); + s+="' has not key '"; + s+=this->ShortNames[col]; + s+="' !"; + throwSimpleException(s.c_str()); + } + } + } + t->AddColumn(newCol); + } + it = this->Nodes.begin(); + vtkJSONInfoNode* in = 0; + for( ; it != this->Nodes.end(); it++ ) { + in = dynamic_cast(*it); + if (in) { + vtkJSONMap& vl = in->GetValues(); + if(vl.size() > 0 ) { + std::string s("Item with name '"); + s+=in->GetName(); + s+="' has unexpected key '"; + s+=vl.begin()->first; + s+="' !"; + throwSimpleException(s.c_str()); + } + } + } +} + +//--------------------------------------------------- +void vtkJSONParser::processQTS() { + this->InsideQuotes = !this->InsideQuotes; + if(this->InsideQuotes) { + addInfo(QTS); + } else { + // Quotes is closed, get content + Info i = this->CInfoVector.back(); + this->CInfoVector.pop_back(); + if(i.type == QTS) { + long begin = i.pos; + long end = ftell(this->File) - 1; + this->LastString = getString(begin, end); + bool parse_list = (this->CInfoVector.size() >= 1 && this->CInfoVector.back().type == OSB); + if(parse_list) { + this->CurrentList.push_back(this->LastString); + } else { + this->Strings.push_back(this->LastString); + processMetaNode(); + } +#ifdef __DEBUG + std::cout<<"String : "<LastString<ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(COLON); + this->ExpectedCharacters.push_back(COMMA); + this->ExpectedCharacters.push_back(CCB); + this->ExpectedCharacters.push_back(CSB); + } + } +} + +//--------------------------------------------------- +void vtkJSONParser::processCOLON() { + if (this->InsideQuotes) + return; + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(OCB); + this->ExpectedCharacters.push_back(QTS); + this->ExpectedCharacters.push_back(OSB); + if(GetInfoNode() && Strings.size() == 1 ) { + allowsDigits(); + } +} + +//--------------------------------------------------- +void vtkJSONParser::processOCB() { + if (this->InsideQuotes) + return; + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(QTS); + this->ExpectedCharacters.push_back(CCB); + + // Create node + if(this->CInfoVector.size() >= 1) { + if ( !GetMetaNode() && this->Strings.size() > 0 && + this->Strings.back() && + strcmp(this->Strings.back(), MD) == 0 ) { +#ifdef __DEBUG + std::cout<<"Create new Meta Node !!!"<CurrentNode = new vtkJSONMetaNode(); + delete this->Strings.back(); + this->Strings.back() = 0; + this->Strings.pop_back(); + } else { + if(this->CInfoVector.back().type == OSB ) { + this->ParseObjectList = true; + } +#ifdef __DEBUG + std::cout<<"Create new Node with name '"<<(this->Strings.size() == 0 ? "" : this->Strings.back())<<"' !!!"<CurrentNode = new vtkJSONInfoNode(this->Strings.size() == 0 ? "" : this->Strings.back()); + if(!this->ParseObjectList && this->Strings.size() == 1) { + this->Strings.pop_back(); + } + } + } + addInfo(OCB); +} + +//--------------------------------------------------- +vtkJSONMetaNode* vtkJSONParser::GetMetaNode() { + vtkJSONMetaNode *mnode = 0; + if( this->CurrentNode ) { + mnode = dynamic_cast(this->CurrentNode); + } + return mnode; +} + +//--------------------------------------------------- +vtkJSONInfoNode* vtkJSONParser::GetInfoNode() { + vtkJSONInfoNode *mnode = 0; + if( this->CurrentNode ) { + mnode = dynamic_cast(this->CurrentNode); + } + return mnode; +} + +//--------------------------------------------------- +void vtkJSONParser::processENDL() { + if(this->InsideQuotes) { + + throwException("quote is not closed !"); + } + this->LineNumber++; + this->ColumnNumber=0; +} + + +//--------------------------------------------------- +void vtkJSONParser::processCSB() { + if (this->InsideQuotes) + return; + + if(this->CInfoVector.back().type == OSB) { + if(this->ParseList) { + this->ParseList = false; + } + if(this->ParseObjectList) { + this->ParseObjectList = false; + } + this->CInfoVector.pop_back(); + } + + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(COMMA); + this->ExpectedCharacters.push_back(OCB); + this->ExpectedCharacters.push_back(CCB); +} + +//--------------------------------------------------- +void vtkJSONParser::processOSB() { + if (this->InsideQuotes) + return; + + if(this->ParseList) { + Info i = CInfoVector.back(); + if(i.type == OSB) { + throwException("list, which has been opened in this place, has not been closed !", i.ln, i.cn); + } + } + if(GetMetaNode()) { + this->ParseList = true; + } else { + if( this->Strings.size() > 0 ) { + delete this->Strings[Strings.size()-1]; + this->Strings[Strings.size()-1] = 0; + this->Strings.pop_back(); + } + } + addInfo(OSB); + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(QTS); + this->ExpectedCharacters.push_back(OCB); +} + +//--------------------------------------------------- +void vtkJSONParser::processCCB() { + if (this->InsideQuotes) + return; + + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(COMMA); + this->ExpectedCharacters.push_back(CCB); + if(this->ParseObjectList) { + this->ExpectedCharacters.push_back(CSB); + } + + processMetaNode(); + processInfoNode(); + + if(this->CurrentNode) + this->Nodes.push_back(this->CurrentNode); + if( !this->ShortNamesFilled ){ + vtkJSONInfoNode* n = dynamic_cast(this->CurrentNode); + if(n){ + this->ShortNamesFilled = true; + } + } + +#ifdef __DEBUG + if(this->CurrentNode) + std::cout<<"End parsing node with name '"<CurrentNode->GetName()<<"' !!!!"<CInfoVector.size() > 0 && this->CInfoVector.back().type == OCB ) { + this->CInfoVector.pop_back(); + } else{ + throwException ("unexpected closed braket '}' !"); + } + + this->CurrentNode = 0; +} + +//--------------------------------------------------- +void vtkJSONParser::processCOMMA() { + if (this->InsideQuotes) + return; + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(QTS); + this->ExpectedCharacters.push_back(OCB); + processMetaNode(); + processInfoNode(); +} + +void vtkJSONParser::processMetaNode() { + vtkJSONMetaNode* mn = GetMetaNode(); + if(mn) { + bool strings = this->Strings.size() == 2 && !this->ParseList; + bool str_and_list = (this->Strings.size() == 1 && this->CurrentList.size() > 0 && !this->ParseList); + + if( strings ) { + if ( strcmp(this->Strings[0], CMT) == 0 ) { + mn->SetComment(this->Strings[1]); +#ifdef __DEBUG + std::cout<<"Table Comment : "<Strings[1]<Strings[0], TBN) == 0 ) { + mn->SetTableName(this->Strings[1]); +#ifdef __DEBUG + std::cout<<"Table Name : "<Strings[1]<Strings[0], TBD) == 0 ) { + mn->SetTableDescription(this->Strings[1]); +#ifdef __DEBUG + std::cout<<"Table Description : "<Strings[1]<Strings[0], DT) == 0 ) { + mn->SetDate(this->Strings[1]); +#ifdef __DEBUG + std::cout<<"Date : "<Strings[1]<FileName; + s << ", line "<LineNumber; + s << " unexpected key world: '"<Strings[0]<<"'"; + } + delete this->Strings[0]; + this->Strings[0] = 0; + Strings.pop_back(); + Strings.pop_back(); + + } else if(str_and_list) { + if ( strcmp(this->Strings[0], SHT) == 0 ) { + std::vector::const_iterator it = this->CurrentList.begin(); + for( ;it != this->CurrentList.end(); it++) { + checkShortName(*it); + } + mn->SetShortNames(this->CurrentList); +#ifdef __DEBUG + std::cout<<"Short Names : "<Strings[0], LNG) == 0 ) { + mn->SetLongNames(this->CurrentList); +#ifdef __DEBUG + std::cout<<"Long Names : "<Strings[0], UNT) == 0 ) { + mn->SetUnits(this->CurrentList); +#ifdef __DEBUG + std::cout<<"Units : "; +#endif + } else { + std::stringstream s; + s << "File : "<< this->FileName; + s << ", line "<LineNumber; + s << " unexpected key world: '"<Strings[0]<<"'"; + } + delete this->Strings[0]; + this->Strings[0] = 0; + Strings.pop_back(); +#ifdef __DEBUG + std::vector::const_iterator it = this->CurrentList.begin(); + std::cout<<"[ "; + for( ;it != this->CurrentList.end(); it++) { + std::cout<<"'"<<*it<<"'"; + if ( it+1 != this->CurrentList.end() ) + std::cout<<", "; + } + std::cout<<" ]"<CurrentList.clear(); + } + } +} +//--------------------------------------------------- +void vtkJSONParser::processInfoNode() { + vtkJSONInfoNode* in = GetInfoNode(); + if(in) { + if(this->Strings.size() == 1 && !isnan(this->LastValue)) { + in->AddValue(this->Strings[0],this->LastValue); + if(!ShortNamesFilled) { + char* name = new char[strlen(Strings[0])]; + strcpy(name, Strings[0]); + this->ShortNames.push_back(name); + } + this->Strings.pop_back(); + this->LastValue = NAN; + } + } +} + +//--------------------------------------------------- +void vtkJSONParser::processCharacter(const char ch) { + if (this->InsideQuotes) + return; + if(isBlankOrEnd(ch)) + return; + + if(this->ExpectedCharacters.empty()) + return; + + std::vector::const_iterator it = std::find(this->ExpectedCharacters.begin(), + this->ExpectedCharacters.end(), + ch); + + // Unexpected character is found + if(it == this->ExpectedCharacters.end()) { + std::string s("unexpected character '"); + s+=ch; + s+="' !"; + throwException(s.c_str()); + + } +} + +//--------------------------------------------------- +char* vtkJSONParser::getString(long b, long e) { + char* result = 0; + + long old_pos = ftell(this->File); + fseek(this->File, b, SEEK_SET); + long data_s = e - b; + result = new char[data_s]; + result[0] = 0; + size_t nb_read = fread(result, sizeof(char), data_s, this->File); + result[nb_read] = '\0'; + fseek(this->File, old_pos, SEEK_SET); + return result; +} + +//--------------------------------------------------- +void vtkJSONParser::allowsDigits() { + for(char c = '.'; c <= '9'; c++) { + ExpectedCharacters.push_back(c); + } + ExpectedCharacters.push_back('-'); + ExpectedCharacters.push_back('+'); + ExpectedCharacters.push_back('e'); +} + +//--------------------------------------------------- +bool vtkJSONParser::isDigitsAllowed() { + std::vector::const_iterator it = std::find(this->ExpectedCharacters.begin(), + this->ExpectedCharacters.end(), + '0'); + return (it != this->ExpectedCharacters.end()); +} + +//--------------------------------------------------- +void vtkJSONParser::readDoubleValue() { + long b = ftell(this->File); + + while(1) { + char ch = fgetc(this->File); + if(!isDigitOrDot(ch)) { + break; + } + } + + long e = ftell(this->File); + fseek(this->File, b-1, SEEK_SET); + long data_s = e - b; + char* result = new char[data_s]; + result[0] = 0; + size_t nb_read = fread(result, sizeof(char), data_s, this->File); + result[nb_read] = '\0'; + this->ExpectedCharacters.clear(); + this->ExpectedCharacters.push_back(COMMA); + this->ExpectedCharacters.push_back(CCB); + this->LastValue = atof(result); +#ifdef __DEBUG + std::cout<<"Read number : "<LastValue< 0 ){ + for( size_t i = 0; i < ln; i++ ) { + // a - z + if(!(name[i] >= 'a' && name[i] <= 'z') && + // A - Z + !(name[i] >= 'A' && name[i] <= 'Z') ) { + std::string s("wrong short name '"); + s += name; + s += "' !"; + throwException(s.c_str(), this->LineNumber); + } + } + } +} + +void vtkJSONParser::clean() { + std::vector::iterator it = this->Nodes.begin(); + for( ; it != this->Nodes.end(); it++ ) { + delete *(it); + *it = 0; + } +} + +//--------------------------------------------------- +void vtkJSONParser::throwException(const char* message) { + std::stringstream s; + s << "File : "<< this->FileName; + s << ", line "<LineNumber; + s << ", column " << this->ColumnNumber<<" : "; + s << message; + clean(); + throw vtkJSONException(s.str().c_str()); +} + +//--------------------------------------------------- +void vtkJSONParser::throwException(const char* message, int ln, int cn) { + std::stringstream s; + s << "File : "<< this->FileName; + s << ", line "<FileName; + s << ", line "<