+++ /dev/null
-/*
-==================================================================================
-
- Copyright (c) 2018-2019 AT&T Intellectual Property.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================================
-*/
-/*
- Author : Ashwin Sridharan
-
-
-*/
-
-#include <json_handler.hpp>
-#include <cstdio>
-
-TrieNode::TrieNode(int val): val_type(-1){
- _id.tag = DataContainer::Types::integer;
- _id.value.i = val;
-}
-
-TrieNode::TrieNode( std::string val) : val_type(-1){
- _id.tag = DataContainer::Types::str;
- _id.value.s.assign(val);
-}
-
-void TrieNode::set_value(const char * val){
- _val.tag = DataContainer::Types::str;
- _val.value.s.assign(val);
-}
-
-
-void TrieNode::set_value(bool val){
- _val.tag = DataContainer::Types::boolean;
- _val.value.b = val;
-
-}
-
-void TrieNode::set_value(int val){
- _val.tag = DataContainer::Types::integer;
- _val.value.i = val;
- //std::cout <<"Assigned integer " << val << std::endl;
-
-
-}
-
-void TrieNode::set_value(unsigned int val){
- _val.tag = DataContainer::Types::uinteger;
- _val.value.ui = val;
-
-}
-
-void TrieNode::set_value( long int val){
- _val.tag = DataContainer::Types::big_integer;
- _val.value.l = val;
-}
-
-void TrieNode::set_value(unsigned long int val){
- _val.tag = DataContainer::Types::ubig_integer;
- _val.value.ul = val;
-}
-
-void TrieNode::set_value(double val){
- _val.tag = DataContainer::Types::real;
- _val.value.f = val;
-
-}
-
-
-void TrieNode::set_value(std::string val){
- _val.tag = DataContainer::Types::str;
- _val.value.s.assign(val);
-}
-
-void TrieNode::set_value(const char * c, size_t len){
- _val.tag = DataContainer::Types::str;
- _val.value.s.assign(c, len);
-}
-
-void TrieNode::add_child(TrieNode * node){
- _children.push_back(node);
-};
-
-
-
-void TrieNode::print_id(void){
- switch(_id.tag){
-
- case DataContainer::Types::integer :
- std::cout <<"Type = " << _id.tag << " Value = " << _id.value.i << std::endl;
- break;
- case DataContainer::Types::str :
- std::cout <<"Type = " << _id.tag << " Value = " << _id.value.s << std::endl;
- break;
- default:
- std::cerr<< "Error ! ID not set or unknown type " << _id.tag;
- }
-};
-
-void TrieNode::print_value(void){
- switch(_val.tag){
-
- case DataContainer::Types::boolean :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.b << std::endl;
- break;
- case DataContainer::Types::integer :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.i << std::endl;
- break;
- case DataContainer::Types::uinteger :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.ui << std::endl;
- break;
- case DataContainer::Types::big_integer :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.l << std::endl;
- break;
- case DataContainer::Types::ubig_integer :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.ul << std::endl;
- break;
- case DataContainer::Types::real :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.f << std::endl;
- break;
- case DataContainer::Types::str :
- std::cout <<"Type = " << _val.tag << " Value = " << _val.value.s << std::endl;
- break;
- default:
- std::cerr<< "Error ! Value not set or unknown type " << _val.tag;
- }
-};
-
-
-jsonHandler::jsonHandler(void):_is_root(false), _is_schema(false), _is_buffer(false){
-
-};
-
-
-
-
-void jsonHandler::load_file(std::string input_file, std::string & contents ){
-
- std::FILE *fp ;
- try{
- fp = std::fopen(input_file.c_str(), "rb");
- }
- catch(std::exception &e){
- std::string error_string = "Error opening input schema file " + input_file;
- throw std::runtime_error(error_string);
- }
-
- if (fp){
- std::fseek(fp, 0, SEEK_END);
- contents.resize(std::ftell(fp));
- std::rewind(fp);
- std::fread(&contents[0], 1, contents.size(), fp);
- std::fclose(fp);
- }
-
- else{
- std::string error_string = "Error opening input file " + input_file;
- throw std::runtime_error(error_string);
- }
-
-}
-
-
-
-void jsonHandler::load_schema(std::string input_file){
-
- load_file(input_file, _contents);
- Document _doc;
- if (_doc.Parse(_contents.c_str()).HasParseError()){
- std::string error_string = input_file + " is invalid JSON" ;
- throw std::runtime_error(error_string);
- }
-
- _ref_schema_doc= std::make_unique<SchemaDocument>(_doc);
- _is_schema = true;
-
-
-}
-
-void jsonHandler::load_schema(std::string input_file, TrieNode * root){
-
- load_file(input_file, _contents);
- std::string response;
- Document _doc;
- if (_doc.Parse(_contents.c_str()).HasParseError()){
- std::string error_string = input_file + " is invalid JSON" ;
- throw std::runtime_error(error_string);
- }
-
- // Get message schema
- bool res;
- Value _schema_root;
- Value &_root = _doc;
- res = find_member(_root, response, root, _schema_root);
- if (res == false){
- throw std::runtime_error(response);
- }
-
- _ref_schema_doc= std::make_unique<SchemaDocument>(_schema_root);
- _is_schema = true;
-
-}
-
-void jsonHandler::load_buffer(std::string input_file){
-
- load_file(input_file, _buffer);
- Document _doc;
- if (_doc.Parse(_buffer.c_str()).HasParseError()){
- std::string error_string = input_file + " is invalid JSON" ;
- throw std::runtime_error(error_string);
- }
- _is_buffer = true;
-
-}
-
-void jsonHandler::load_buffer(std::string input_file, TrieNode * root){
-
- load_file(input_file, _buffer);
- Document _doc;
- std::string response;
-
- if (_doc.Parse(_buffer.c_str()).HasParseError()){
- std::string error_string = input_file + " is invalid JSON" ;
- throw std::runtime_error(error_string);
- }
-
- bool res;
- Value _buffer_root;
- res = find_member(_doc, response, root, _buffer_root);
- if(res == false){
- throw std::runtime_error(response);
- }
-
- StringBuffer out_buffer;
- Writer<StringBuffer> writer(out_buffer);
- _buffer_root.Accept(writer);
- _buffer.assign(out_buffer.GetString(), out_buffer.GetLength());
- _is_buffer = true;
-}
-
-
-std::string jsonHandler::get_buffer(void){
- std::string response;
- if (_is_buffer){
- response.assign(_buffer);
- }
- else{
- response = "";
- }
-
- return response;
-}
-
-
-
-
-
-
-bool jsonHandler::find_member(const std::string schema, std::string & response, TrieNode * root, Value & TargetVal){
-
- Document doc;
- std::string contents(schema);
-
- if(doc.Parse(contents.c_str()).HasParseError()){
- response.assign("Error Parsing JSON File");
- return false;
- }
-
- return find_member(doc, response, root, TargetVal);
- return true;
-};
-
-
-bool jsonHandler::find_member(Value & doc_root, std::string & response, TrieNode * root, Value & TargetVal){
-
- if (!root){
- response.assign("Null Trie root node");
- return false;
- }
- //std::cout <<"LOoking for schema root" << std::endl;
-
- Value & json_node = doc_root;
- TrieNode * trie_node = root;
- Value::MemberIterator itr;
-
- while(1){
-
- DataContainer const * d = trie_node->get_id();
- if (! d){
- response.assign("Error could not find any id for trie node ");
- return false;
- }
-
- if (d->tag == DataContainer::Types::integer && json_node.IsArray()){
- if (json_node.Size() < d->value.i){
- response.assign("Error json array size ");
- response += std::to_string(json_node.Size()) + " is smaller than trie node index " + std::to_string( d->value.i);
- return false;
- }
-
- if (trie_node->is_child()){
- response.assign("Error child trie points to an array ? ");
- return false;
- }
-
- trie_node = trie_node->get_children()[0];
- json_node = json_node[d->value.i];
- }
- else if (d->tag == DataContainer::Types::str && json_node.IsObject()){
-
- itr = json_node.FindMember(d->value.s.c_str());
- if (itr == json_node.MemberEnd()){
- response.assign("Error ! Could not find key = ");
- response += d->value.s;
- return false;
- }
- if (trie_node->is_child()){
- // reached end of trie
- if (itr->value.IsObject()){
- TargetVal = itr->value.GetObject();
- //std::cout <<"Reached root = " << itr->name.GetString() << std::endl;
- }
- else if (itr->value.IsArray()){
- TargetVal = itr->value.GetArray();
- }
- else{
- response.assign("Error ! JSON node selected must be object or array in current version");
- std::cerr << response << std::endl;
- return false;
- }
- break;
- }
- else{
- trie_node = trie_node->get_children()[0];
- trie_node->print_id();
-
- if (itr->value.IsObject()){
- json_node = itr->value.GetObject();
- }
- else if (itr->value.IsArray()){
- json_node = itr->value.GetArray();
- }
- else{
- std::string error_string= " Path must be an object or array";
- response.assign(error_string);
- return false;
- }
- }
- }
- else{
- std::string error_string = "Mismatch when setting root : Trie node is of type = " + std::to_string (d->tag) + " and json node is of type = " + std::to_string(json_node.GetType());
- response.assign(error_string);
- return false;
- }
-
- }
-
- return true;
-
-}
-
-bool jsonHandler::is_valid(const char *message, int message_length, std::string & response){
-
- Document doc;
- if (! _is_schema){
- return false;
- }
-
- SchemaValidator validator(*(_ref_schema_doc.get()));
-
- // ensure message has terminator by translating to string ?
- std::string message_s(message, message_length);
-
-
- // validate json
- if (doc.Parse(message_s.c_str()).HasParseError()){
-
- // return error message
- std::string failed_message = "\"message\": \"Invalid JSON\"";
- response.assign( failed_message );
- return false;
- }
-
-
- // Validate against our JSON input schema
- if (!doc.Accept(validator)){
-
- StringBuffer sb;
- validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
- std::string failed_message = std::string("\"message\": \"Schema Violation:") + std::string(sb.GetString());
- failed_message += std::string(" Invalid keyword :") + std::string(validator.GetInvalidSchemaKeyword()) + " \"";
- response.assign(failed_message);
- return false;
-
- }
- response.assign("SUCCESS");
- return true;
-
-
-}
-
-
-
-// should be thread safe since it can be expected to be called from multiple threads
-// only static external variable referenced is the schema (which should be read-only and hence ok ?)
-
-// Returns 0 if success
-// -1 if invalid json
-// -2 if invalid schema (assuming schema provided)
-// -3 unknown key
-// -4 no buffer available
-
-int jsonHandler::get_values(const char *message, int message_length, std::string & response, TrieNode * root, std::vector<TrieNode *> & response_list){
-
- Document doc;
-
- // ensure message has terminator by translating to string ?
- std::string message_s(message, message_length);
-
- // validate json
- if (doc.Parse(message_s.c_str()).HasParseError()){
-
- // return error message
- response.assign("Invalid JSON");
- return -1;
- }
-
- // Validate against our JSON input schema
- if ( _is_schema){
- SchemaValidator validator(*(_ref_schema_doc.get()));
-
- if (!doc.Accept(validator)){
-
- StringBuffer sb;
- validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
- response = std::string("Schema Violation ") + std::string(sb.GetString()) ;
- response += std::string(" Invalid keyword = ") + std::string(validator.GetInvalidSchemaKeyword()) + " \"";
- return -2;
- }
-
- }
-
- Value & doc_root = doc;
- bool res = traverse_doc(doc_root, root, true, response, response_list);
- if (!res){
- return -3;
- }
-
- response.assign("SUCCESS");
- return 0;
-}
-
-
-int jsonHandler::get_values( std::string & response, TrieNode * root, std::vector<TrieNode *> & response_list){
- int res;
- if (_is_buffer){
- Document _doc;
- _doc.Parse(_buffer.c_str());
- Value & _buffer_root = _doc;
-
- res = traverse_doc(_buffer_root, root, true, response, response_list);
- return res;
- }
- else{
- response = "Error ! No buffer loaded in json object for get";
- return -4;
- }
-}
-
-
-// If in get mode, return all values we can get
-// If in set mode, return false if we cannot set a value
-bool jsonHandler::traverse_doc(Value & json_node, TrieNode * trie_node, bool get, std::string & response, std::vector<TrieNode* > & response_list ){
-
- if (!trie_node){
- response.assign(" Null Trie node ");
- return false;
- }
-
- bool res;
-
- DataContainer const * d = trie_node->get_id();
- if (! d){
- response.assign(" Error could not find any id for trie node");
- return false;
- }
-
- Value::MemberIterator itr;
-
-
- if (d->tag == DataContainer::Types::integer && json_node.IsArray()){
- if (json_node.Size() < d->value.i){
- response = "Error json array size " + std::to_string( json_node.Size()) + " is smaller than trie node index " + std::to_string( d->value.i);
- return false;
- }
-
- if (trie_node->is_child()){
- response.assign("Error child trie points to an array ? ");
- return false;
- }
-
- for (auto & e: trie_node->get_children()){
- res = traverse_doc(json_node[d->value.i], e, get, response, response_list);
- if (!res && ! get){
- // if not in get mode and we hit a not found
- // don't go any further, else move to next ...
- return res;
- }
- }
- }
-
- else if (d->tag == DataContainer::Types::str && json_node.IsObject()){
- itr = json_node.FindMember(d->value.s.c_str());
-
- if (itr == json_node.MemberEnd()){
- response = "Error ! Could not find key " + d->value.s;
- return false;
- }
-
- if (trie_node->is_child()){
- // end of the line : do we get or set values ?
- bool is_set = false;
- if (get){
- if (itr->value.IsBool()){
- trie_node->set_value(itr->value.GetBool());
- is_set = true;
- }
- else if (itr->value.IsInt()){
- trie_node->set_value(itr->value.GetInt());
- is_set = true;
- }
- else if(itr->value.IsUint()){
- trie_node->set_value(static_cast<unsigned int>(itr->value.GetUint()));
- is_set = true;
- }
- else if(itr->value.IsUint64()){
- trie_node->set_value(static_cast<unsigned long int>(itr->value.GetUint64()));
- is_set = true;
- }
- else if (itr->value.IsInt64()){
- trie_node->set_value(static_cast<long int>(itr->value.GetInt64()));
- is_set = true;
- }
- else if ( itr->value.IsDouble()){
- trie_node->set_value(itr->value.GetDouble());
- is_set = true;
- }
- else if ( itr->value.IsString()){
- trie_node->set_value(itr->value.GetString(), itr->value.GetStringLength());
- is_set = true;
- }
- else{
- response = " json node corresponding to child node key must of type bool, int or string. Is of type = " + std::to_string(itr->value.GetType());
- return false;
- }
-
- if (is_set){
- response_list.push_back(trie_node);
- }
-
- //std::cout <<"Set value of child node with key = " << d->value.s.c_str() << " Type = " << trie_node->get_type() << std::endl;
-
- }
- else{
- DataContainer const * d_val = trie_node->get_value();
- if (d_val->tag == DataContainer::Types::boolean){
- itr->value.SetBool(d_val->value.b);
- }
- else if (d_val->tag == DataContainer::Types::integer){
- itr->value.SetInt(d_val->value.i);
-
- }
- else if (d_val->tag == DataContainer::Types::uinteger){
- itr->value.SetUint(d_val->value.ui);
-
- }
- else if (d_val->tag == DataContainer::Types::big_integer){
- itr->value.SetInt64(d_val->value.l);
- }
- else if (d_val->tag == DataContainer::Types::ubig_integer){
- itr->value.SetUint64(d_val->value.ul);
- }
- else if (d_val->tag == DataContainer::Types::real){
- itr->value.SetDouble(d_val->value.f);
- }
- else if (d_val->tag == DataContainer::Types::str){
- itr->value.SetString(d_val->value.s.c_str(), d_val->value.s.length());
- }
- else{
- response = " unknown type for child node value = " + std::to_string(d_val->tag) + " cannot set json node key = " + d->value.s;
- return false;
- }
- }
- return true;
- }
- else{
- for (auto & e: trie_node->get_children()){
- res = traverse_doc(itr->value, e, get, response, response_list);
- if(res == false && ! get){
- return false;
- }
- }
- }
- }
-
- else{
- response = "Mismatch : Trie node is of type = " + std::to_string(d->tag) + " while json node is of type = " + std::to_string( json_node.GetType());
- return false;
- }
-
- return true;
-
-
-}
-
-
-int jsonHandler::set_values(const char * buffer, int len, std::string & response, std::vector<TrieNode *> root_nodes){
-
- Document doc;
- std::string message_s(buffer, len);
-
- // validate json
- if (doc.Parse(message_s.c_str()).HasParseError()){
- // return error message
- response.assign("Invalid JSON");
- return -1;
- }
-
-
- Value & doc_root = doc;
- // fake list to maintain signature for re-using traverse_doc
- // since we don't return trie nodes when setting ...
- std::vector<TrieNode *> fake_list;
- for(auto const & e: root_nodes){
- bool res = traverse_doc(doc_root, e, false, response, fake_list);
- if (!res){
- return -3;
- }
- }
-
- StringBuffer out_buffer;
- Writer<StringBuffer> writer(out_buffer);
- doc_root.Accept(writer);
- response.assign(out_buffer.GetString(), out_buffer.GetLength());
- return 0;
- }
-
-
-// wrapper if instead of providing buffer, we simply use stored json object and use it
-int jsonHandler::set_values(std::string & response, std::vector<TrieNode *> root_nodes){
- if (_is_buffer){
- std::vector<TrieNode *> fake_list;
- Document _doc;
- _doc.Parse(_buffer.c_str());
- Value & _buffer_root = _doc;
-
- for(auto const & e: root_nodes){
- bool res = traverse_doc(_buffer_root, e, false, response, fake_list);
- if (!res){
- return -3;
- }
- }
-
- StringBuffer out_buffer;
- Writer<StringBuffer> writer(out_buffer);
- _buffer_root.Accept(writer);
- response.assign(out_buffer.GetString(), out_buffer.GetLength());
- return 0;
- }
- else{
- response = "Error ! " + std::string( __FILE__) + "," + std::to_string(__LINE__) + " : No buffer loaded in json object to set";
- return -1;
- }
-
-}