/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * * Contains some contributions under the Thrift Software License. * Please see doc/old-thrift-license.txt in the Thrift distribution for * details. */ #include #include #include #include #include #include #include #include "thrift/platform.h" #include "thrift/generate/t_generator.h" using std::map; using std::ofstream; using std::ostream; using std::ostringstream; using std::string; using std::stringstream; using std::vector; using std::stack; static const bool NO_INDENT = false; static const bool FORCE_STRING = true; class t_json_generator : public t_generator { public: t_json_generator(t_program* program, const std::map& parsed_options, const std::string& option_string) : t_generator(program) { (void)option_string; std::map::const_iterator iter; should_merge_includes_ = false; for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { if( iter->first.compare("merge") == 0) { should_merge_includes_ = true; } else { throw "unknown option json:" + iter->first; } } out_dir_base_ = "gen-json"; } ~t_json_generator() override = default; /** * Init and close methods */ void init_generator() override; void close_generator() override; std::string display_name() const override; void generate_typedef(t_typedef* ttypedef) override; void generate_enum(t_enum* tenum) override; void generate_program() override; void generate_function(t_function* tfunc); void generate_field(t_field* field); void generate_service(t_service* tservice) override; void generate_struct(t_struct* tstruct) override; private: bool should_merge_includes_; ofstream_with_content_based_conditional_update f_json_; std::stack comma_needed_; template string number_to_string(T t) { std::ostringstream out; out.imbue(std::locale::classic()); out.precision(std::numeric_limits::digits10); out << t; return out.str(); } template void write_number(T n) { f_json_ << number_to_string(n); } string get_type_name(t_type* ttype); string get_qualified_name(t_type* ttype); void start_object(bool should_indent = true); void start_array(); void end_object(); void end_array(); void write_comma_if_needed(); void indicate_comma_needed(); string escape_json_string(const string& input); string json_str(const string& str); void merge_includes(t_program*); void generate_constant(t_const* con); void write_type_spec_entry(const char* name, t_type* ttype); void write_type_spec_object(const char* name, t_type* ttype); void write_type_spec(t_type* ttype); void write_string(const string& value); void write_value(t_type* tvalue); void write_const_value(t_const_value* value, bool force_string = false); void write_key_and(string key); void write_key_and_string(string key, string val); void write_key_and_integer(string key, int val); void write_key_and_bool(string key, bool val); }; void t_json_generator::init_generator() { MKDIR(get_out_dir().c_str()); string f_json_name = get_out_dir() + program_->get_name() + ".json"; f_json_.open(f_json_name.c_str()); // Merge all included programs into this one so we can output one big file. if (should_merge_includes_) { merge_includes(program_); } } string t_json_generator::escape_json_string(const string& input) { std::ostringstream ss; for (char iter : input) { switch (iter) { case '\\': ss << "\\\\"; break; case '"': ss << "\\\""; break; case '/': ss << "\\/"; break; case '\b': ss << "\\b"; break; case '\f': ss << "\\f"; break; case '\n': ss << "\\n"; break; case '\r': ss << "\\r"; break; case '\t': ss << "\\t"; break; default: ss << iter; break; } } return ss.str(); } void t_json_generator::start_object(bool should_indent) { f_json_ << (should_indent ? indent() : "") << "{" << '\n'; indent_up(); comma_needed_.push(false); } void t_json_generator::start_array() { f_json_ << "[" << '\n'; indent_up(); comma_needed_.push(false); } void t_json_generator::write_comma_if_needed() { if (comma_needed_.top()) { f_json_ << "," << '\n'; } } void t_json_generator::indicate_comma_needed() { comma_needed_.pop(); comma_needed_.push(true); } void t_json_generator::write_key_and(string key) { write_comma_if_needed(); indent(f_json_) << json_str(key) << ": "; indicate_comma_needed(); } void t_json_generator::write_key_and_integer(string key, int val) { write_comma_if_needed(); indent(f_json_) << json_str(key) << ": " << number_to_string(val); indicate_comma_needed(); } void t_json_generator::write_key_and_string(string key, string val) { write_comma_if_needed(); indent(f_json_) << json_str(key) << ": " << json_str(val); indicate_comma_needed(); } void t_json_generator::write_key_and_bool(string key, bool val) { write_comma_if_needed(); indent(f_json_) << json_str(key) << ": " << (val ? "true" : "false"); indicate_comma_needed(); } void t_json_generator::end_object() { indent_down(); f_json_ << '\n' << indent() << "}"; comma_needed_.pop(); } void t_json_generator::end_array() { indent_down(); if (comma_needed_.top()) { f_json_ << '\n'; } indent(f_json_) << "]"; comma_needed_.pop(); } void t_json_generator::write_type_spec_object(const char* name, t_type* ttype) { ttype = ttype->get_true_type(); if (ttype->is_struct() || ttype->is_xception() || ttype->is_container() || ttype->is_enum()) { write_key_and(name); start_object(NO_INDENT); write_key_and("typeId"); write_type_spec(ttype); end_object(); } } void t_json_generator::write_type_spec_entry(const char* name, t_type* ttype) { write_key_and(name); write_type_spec(ttype); } void t_json_generator::write_type_spec(t_type* ttype) { ttype = ttype->get_true_type(); write_string(get_type_name(ttype)); if (ttype->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : ttype->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } if (ttype->is_struct() || ttype->is_xception() || ttype->is_enum()) { write_key_and_string("class", get_qualified_name(ttype)); } else if (ttype->is_map()) { t_type* ktype = ((t_map*)ttype)->get_key_type(); t_type* vtype = ((t_map*)ttype)->get_val_type(); write_key_and_string("keyTypeId", get_type_name(ktype)); write_key_and_string("valueTypeId", get_type_name(vtype)); write_type_spec_object("keyType", ktype); write_type_spec_object("valueType", vtype); } else if (ttype->is_list()) { t_type* etype = ((t_list*)ttype)->get_elem_type(); write_key_and_string("elemTypeId", get_type_name(etype)); write_type_spec_object("elemType", etype); } else if (ttype->is_set()) { t_type* etype = ((t_set*)ttype)->get_elem_type(); write_key_and_string("elemTypeId", get_type_name(etype)); write_type_spec_object("elemType", etype); } } void t_json_generator::close_generator() { f_json_ << '\n'; f_json_.close(); } void t_json_generator::merge_includes(t_program* program) { vector includes = program->get_includes(); vector::iterator inc_iter; for (inc_iter = includes.begin(); inc_iter != includes.end(); ++inc_iter) { t_program* include = *inc_iter; // recurse in case we get crazy merge_includes(include); // merge enums vector enums = include->get_enums(); vector::iterator en_iter; for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { program->add_enum(*en_iter); } // merge typedefs vector typedefs = include->get_typedefs(); vector::iterator td_iter; for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { program->add_typedef(*td_iter); } // merge structs vector objects = include->get_objects(); vector::iterator o_iter; for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { program->add_struct(*o_iter); } // merge constants vector consts = include->get_consts(); vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { program->add_const(*c_iter); } // merge services vector services = include->get_services(); vector::iterator sv_iter; for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { program->add_service(*sv_iter); } } } void t_json_generator::generate_program() { init_generator(); start_object(); write_key_and_string("name", program_->get_name()); if (program_->has_doc()) { write_key_and_string("doc", program_->get_doc()); } // When merging includes, the "namespaces" and "includes" sections // become ambiguous, so just skip them. if (!should_merge_includes_) { // Generate namespaces write_key_and("namespaces"); start_object(NO_INDENT); const map& namespaces = program_->get_namespaces(); map::const_iterator ns_it; for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) { write_key_and_string(ns_it->first, ns_it->second); indicate_comma_needed(); } end_object(); // Generate includes write_key_and("includes"); start_array(); const vector includes = program_->get_includes(); vector::const_iterator inc_it; for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) { write_comma_if_needed(); f_json_ << indent(); write_string((*inc_it)->get_name()); indicate_comma_needed(); } end_array(); } // Generate enums write_key_and("enums"); start_array(); vector enums = program_->get_enums(); vector::iterator en_iter; for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { write_comma_if_needed(); generate_enum(*en_iter); indicate_comma_needed(); } end_array(); // Generate typedefs write_key_and("typedefs"); start_array(); vector typedefs = program_->get_typedefs(); vector::iterator td_iter; for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { write_comma_if_needed(); generate_typedef(*td_iter); indicate_comma_needed(); } end_array(); // Generate structs, exceptions, and unions in declared order write_key_and("structs"); start_array(); vector objects = program_->get_objects(); vector::iterator o_iter; for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { write_comma_if_needed(); if ((*o_iter)->is_xception()) { generate_xception(*o_iter); } else { generate_struct(*o_iter); } indicate_comma_needed(); } end_array(); // Generate constants write_key_and("constants"); start_array(); vector consts = program_->get_consts(); vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { write_comma_if_needed(); generate_constant(*c_iter); indicate_comma_needed(); } end_array(); // Generate services write_key_and("services"); start_array(); vector services = program_->get_services(); vector::iterator sv_iter; for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { write_comma_if_needed(); generate_service(*sv_iter); indicate_comma_needed(); } end_array(); end_object(); // Close the generator close_generator(); } void t_json_generator::generate_typedef(t_typedef* ttypedef) { start_object(); write_key_and_string("name", get_qualified_name(ttypedef)); write_key_and_string("typeId", get_type_name(ttypedef->get_true_type())); write_type_spec_object("type", ttypedef->get_true_type()); if (ttypedef->has_doc()) { write_key_and_string("doc", ttypedef->get_doc()); } if (ttypedef->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : ttypedef->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } end_object(); } void t_json_generator::write_string(const string& value) { f_json_ << '\"' << escape_json_string(value) << '\"'; } void t_json_generator::write_const_value(t_const_value* value, bool should_force_string) { switch (value->get_type()) { case t_const_value::CV_IDENTIFIER: case t_const_value::CV_INTEGER: if (should_force_string) { write_string(number_to_string(value->get_integer())); } else { write_number(value->get_integer()); } break; case t_const_value::CV_DOUBLE: if (should_force_string) { write_string(number_to_string(value->get_double())); } else { write_number(value->get_double()); } break; case t_const_value::CV_STRING: write_string(value->get_string()); break; case t_const_value::CV_LIST: { start_array(); std::vector list = value->get_list(); std::vector::iterator lit; for (lit = list.begin(); lit != list.end(); ++lit) { write_comma_if_needed(); f_json_ << indent(); write_const_value(*lit); indicate_comma_needed(); } end_array(); break; } case t_const_value::CV_MAP: { start_object(NO_INDENT); std::map map = value->get_map(); std::map::iterator mit; for (mit = map.begin(); mit != map.end(); ++mit) { write_comma_if_needed(); f_json_ << indent(); // JSON objects only allow string keys write_const_value(mit->first, FORCE_STRING); f_json_ << ": "; write_const_value(mit->second); indicate_comma_needed(); } end_object(); break; } default: f_json_ << "null"; break; } } string t_json_generator::json_str(const string& str) { return string("\"") + escape_json_string(str) + string("\""); } void t_json_generator::generate_constant(t_const* con) { start_object(); write_key_and_string("name", con->get_name()); write_key_and_string("typeId", get_type_name(con->get_type())); write_type_spec_object("type", con->get_type()); if (con->has_doc()) { write_key_and_string("doc", con->get_doc()); } write_key_and("value"); write_const_value(con->get_value()); end_object(); } void t_json_generator::generate_enum(t_enum* tenum) { start_object(); write_key_and_string("name", tenum->get_name()); if (tenum->has_doc()) { write_key_and_string("doc", tenum->get_doc()); } if (tenum->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : tenum->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } write_key_and("members"); start_array(); vector values = tenum->get_constants(); vector::iterator val_iter; for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { write_comma_if_needed(); t_enum_value* val = (*val_iter); start_object(); write_key_and_string("name", val->get_name()); write_key_and_integer("value", val->get_value()); if (val->has_doc()) { write_key_and_string("doc", val->get_doc()); } end_object(); indicate_comma_needed(); } end_array(); end_object(); } void t_json_generator::generate_struct(t_struct* tstruct) { start_object(); write_key_and_string("name", tstruct->get_name()); if (tstruct->has_doc()) { write_key_and_string("doc", tstruct->get_doc()); } if (tstruct->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : tstruct->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } write_key_and_bool("isException", tstruct->is_xception()); write_key_and_bool("isUnion", tstruct->is_union()); write_key_and("fields"); start_array(); vector members = tstruct->get_members(); vector::iterator mem_iter; for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) { write_comma_if_needed(); generate_field(*mem_iter); indicate_comma_needed(); } end_array(); end_object(); } void t_json_generator::generate_service(t_service* tservice) { start_object(); write_key_and_string("name", get_qualified_name(tservice)); if (tservice->get_extends()) { write_key_and_string("extends", get_qualified_name(tservice->get_extends())); } if (tservice->has_doc()) { write_key_and_string("doc", tservice->get_doc()); } if (tservice->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : tservice->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } write_key_and("functions"); start_array(); vector functions = tservice->get_functions(); vector::iterator fn_iter = functions.begin(); for (; fn_iter != functions.end(); fn_iter++) { write_comma_if_needed(); generate_function(*fn_iter); indicate_comma_needed(); } end_array(); end_object(); } void t_json_generator::generate_function(t_function* tfunc) { start_object(); write_key_and_string("name", tfunc->get_name()); write_key_and_string("returnTypeId", get_type_name(tfunc->get_returntype())); write_type_spec_object("returnType", tfunc->get_returntype()); write_key_and_bool("oneway", tfunc->is_oneway()); if (tfunc->has_doc()) { write_key_and_string("doc", tfunc->get_doc()); } if (tfunc->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : tfunc->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } write_key_and("arguments"); start_array(); vector members = tfunc->get_arglist()->get_members(); vector::iterator mem_iter = members.begin(); for (; mem_iter != members.end(); mem_iter++) { write_comma_if_needed(); generate_field(*mem_iter); indicate_comma_needed(); } end_array(); write_key_and("exceptions"); start_array(); vector excepts = tfunc->get_xceptions()->get_members(); vector::iterator ex_iter = excepts.begin(); for (; ex_iter != excepts.end(); ex_iter++) { write_comma_if_needed(); generate_field(*ex_iter); indicate_comma_needed(); } end_array(); end_object(); } void t_json_generator::generate_field(t_field* field) { start_object(); write_key_and_integer("key", field->get_key()); write_key_and_string("name", field->get_name()); write_key_and_string("typeId", get_type_name(field->get_type())); write_type_spec_object("type", field->get_type()); if (field->has_doc()) { write_key_and_string("doc", field->get_doc()); } if (field->annotations_.size() > 0) { write_key_and("annotations"); start_object(); for (auto & annotation : field->annotations_) { for (auto& annotation_value : annotation.second) { write_key_and_string(annotation.first, annotation_value); } } end_object(); } write_key_and("required"); switch (field->get_req()) { case t_field::T_REQUIRED: write_string("required"); break; case t_field::T_OPT_IN_REQ_OUT: write_string("req_out"); break; default: write_string("optional"); break; } if (field->get_value()) { write_key_and("default"); write_const_value(field->get_value()); } end_object(); } string t_json_generator::get_type_name(t_type* ttype) { ttype = ttype->get_true_type(); if (ttype->is_list()) { return "list"; } if (ttype->is_set()) { return "set"; } if (ttype->is_map()) { return "map"; } if (ttype->is_enum()) { return "enum"; } if (ttype->is_struct()) { return ((t_struct*)ttype)->is_union() ? "union" : "struct"; } if (ttype->is_xception()) { return "exception"; } if (ttype->is_base_type()) { t_base_type* tbasetype = (t_base_type*)ttype; return tbasetype->is_binary() ? "binary" : t_base_type::t_base_name(tbasetype->get_base()); } return "(unknown)"; } string t_json_generator::get_qualified_name(t_type* ttype) { if (should_merge_includes_ || ttype->get_program() == program_) { return ttype->get_name(); } return ttype->get_program()->get_name() + "." + ttype->get_name(); } std::string t_json_generator::display_name() const { return "JSON"; } THRIFT_REGISTER_GENERATOR(json, "JSON", " merge: Generate output with included files merged\n")