blob: 8dd84f0e13cac2890bfc53d02f19c4bc4ff75a10 [file] [log] [blame]
/*
* 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.
*/
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <sys/stat.h>
#include <sstream>
#include "thrift/version.h"
#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;
/**
* XSD generator, creates an XSD for the base types etc.
*
*/
class t_xsd_generator : public t_generator {
public:
t_xsd_generator(t_program* program,
const std::map<std::string, std::string>& parsed_options,
const std::string& option_string)
: t_generator(program) {
(void)option_string;
std::map<std::string, std::string>::const_iterator iter;
/* no options yet */
for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
throw "unknown option xsd:" + iter->first;
}
out_dir_base_ = "gen-xsd";
}
~t_xsd_generator() override = default;
/**
* Init and close methods
*/
void init_generator() override;
void close_generator() override;
std::string display_name() const override;
/**
* Program-level generation functions
*/
void generate_typedef(t_typedef* ttypedef) override;
void generate_enum(t_enum* tenum) override { (void)tenum; }
void generate_service(t_service* tservice) override;
void generate_struct(t_struct* tstruct) override;
private:
void generate_element(std::ostream& out,
std::string name,
t_type* ttype,
t_struct* attrs = nullptr,
bool optional = false,
bool nillable = false,
bool list_element = false);
std::string ns(std::string in, std::string ns) { return ns + ":" + in; }
std::string xsd(std::string in) { return ns(in, "xsd"); }
std::string type_name(t_type* ttype);
std::string base_type_name(t_base_type::t_base tbase);
virtual std::string xml_autogen_comment() {
return std::string("<!--\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ " -->\n";
}
/**
* Output xsd/php file
*/
ofstream_with_content_based_conditional_update f_xsd_;
ofstream_with_content_based_conditional_update f_php_;
/**
* Output string stream
*/
std::ostringstream s_xsd_types_;
};
void t_xsd_generator::init_generator() {
// Make output directory
MKDIR(get_out_dir().c_str());
// Make output file
string f_php_name = get_out_dir() + program_->get_name() + "_xsd.php";
f_php_.open(f_php_name.c_str());
f_php_ << "<?php" << '\n'
<< autogen_comment() << '\n';
}
void t_xsd_generator::close_generator() {
f_php_ << "?>" << '\n';
f_php_.close();
}
void t_xsd_generator::generate_typedef(t_typedef* ttypedef) {
indent(s_xsd_types_) << "<xsd:simpleType name=\"" << ttypedef->get_name() << "\">" << '\n';
indent_up();
indent(s_xsd_types_) << "<xsd:restriction base=\"" << type_name(ttypedef->get_type()) << "\" />"
<< '\n';
indent_down();
indent(s_xsd_types_) << "</xsd:simpleType>" << '\n' << '\n';
}
void t_xsd_generator::generate_struct(t_struct* tstruct) {
vector<t_field*>::const_iterator m_iter;
const vector<t_field*>& members = tstruct->get_members();
bool xsd_all = tstruct->get_xsd_all();
indent(s_xsd_types_) << "<xsd:complexType name=\"" << tstruct->get_name() << "\">" << '\n';
indent_up();
if (xsd_all) {
indent(s_xsd_types_) << "<xsd:all>" << '\n';
} else {
indent(s_xsd_types_) << "<xsd:sequence>" << '\n';
}
indent_up();
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
generate_element(s_xsd_types_,
(*m_iter)->get_name(),
(*m_iter)->get_type(),
(*m_iter)->get_xsd_attrs(),
(*m_iter)->get_xsd_optional() || xsd_all,
(*m_iter)->get_xsd_nillable());
}
indent_down();
if (xsd_all) {
indent(s_xsd_types_) << "</xsd:all>" << '\n';
} else {
indent(s_xsd_types_) << "</xsd:sequence>" << '\n';
}
indent_down();
indent(s_xsd_types_) << "</xsd:complexType>" << '\n' << '\n';
}
void t_xsd_generator::generate_element(ostream& out,
string name,
t_type* ttype,
t_struct* attrs,
bool optional,
bool nillable,
bool list_element) {
string sminOccurs = (optional || list_element) ? " minOccurs=\"0\"" : "";
string smaxOccurs = list_element ? " maxOccurs=\"unbounded\"" : "";
string soptional = sminOccurs + smaxOccurs;
string snillable = nillable ? " nillable=\"true\"" : "";
if (ttype->is_void() || ttype->is_list()) {
indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">" << '\n';
indent_up();
if (attrs == nullptr && ttype->is_void()) {
indent(out) << "<xsd:complexType />" << '\n';
} else {
indent(out) << "<xsd:complexType>" << '\n';
indent_up();
if (ttype->is_list()) {
indent(out) << "<xsd:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" << '\n';
indent_up();
string subname;
t_type* subtype = ((t_list*)ttype)->get_elem_type();
if (subtype->is_base_type() || subtype->is_container()) {
subname = name + "_elt";
} else {
subname = type_name(subtype);
}
f_php_ << "$GLOBALS['" << program_->get_name() << "_xsd_elt_" << name << "'] = '" << subname
<< "';" << '\n';
generate_element(out, subname, subtype, nullptr, false, false, true);
indent_down();
indent(out) << "</xsd:sequence>" << '\n';
indent(out) << "<xsd:attribute name=\"list\" type=\"xsd:boolean\" />" << '\n';
}
if (attrs != nullptr) {
const vector<t_field*>& members = attrs->get_members();
vector<t_field*>::const_iterator a_iter;
for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
<< type_name((*a_iter)->get_type()) << "\" />" << '\n';
}
}
indent_down();
indent(out) << "</xsd:complexType>" << '\n';
}
indent_down();
indent(out) << "</xsd:element>" << '\n';
} else {
if (attrs == nullptr) {
indent(out) << "<xsd:element name=\"" << name << "\""
<< " type=\"" << type_name(ttype) << "\"" << soptional << snillable << " />"
<< '\n';
} else {
// Wow, all this work for a SIMPLE TYPE with attributes?!?!?!
indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">"
<< '\n';
indent_up();
indent(out) << "<xsd:complexType>" << '\n';
indent_up();
indent(out) << "<xsd:complexContent>" << '\n';
indent_up();
indent(out) << "<xsd:extension base=\"" << type_name(ttype) << "\">" << '\n';
indent_up();
const vector<t_field*>& members = attrs->get_members();
vector<t_field*>::const_iterator a_iter;
for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
<< type_name((*a_iter)->get_type()) << "\" />" << '\n';
}
indent_down();
indent(out) << "</xsd:extension>" << '\n';
indent_down();
indent(out) << "</xsd:complexContent>" << '\n';
indent_down();
indent(out) << "</xsd:complexType>" << '\n';
indent_down();
indent(out) << "</xsd:element>" << '\n';
}
}
}
void t_xsd_generator::generate_service(t_service* tservice) {
// Make output file
string f_xsd_name = get_out_dir() + tservice->get_name() + ".xsd";
f_xsd_.open(f_xsd_name.c_str());
string ns = program_->get_namespace("xsd");
const std::map<std::string, std::vector<std::string>> annot = program_->get_namespace_annotations("xsd");
const std::map<std::string, std::vector<std::string>>::const_iterator uri = annot.find("uri");
if (uri != annot.end() && !uri->second.empty()) {
ns = uri->second.back();
}
if (ns.size() > 0) {
ns = " targetNamespace=\"" + ns + "\" xmlns=\"" + ns + "\" "
+ "elementFormDefault=\"qualified\"";
}
// Print the XSD header
f_xsd_ << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << '\n'
<< "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" << ns << ">" << '\n'
<< xml_autogen_comment()
<< '\n';
// Print out the type definitions
indent(f_xsd_) << s_xsd_types_.str();
// Keep a list of all the possible exceptions that might get thrown
map<string, t_struct*> all_xceptions;
// List the elements that you might actually get
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
string elemname = (*f_iter)->get_name() + "_response";
t_type* returntype = (*f_iter)->get_returntype();
generate_element(f_xsd_, elemname, returntype);
f_xsd_ << '\n';
t_struct* xs = (*f_iter)->get_xceptions();
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
all_xceptions[(*x_iter)->get_name()] = (t_struct*)((*x_iter)->get_type());
}
}
map<string, t_struct*>::iterator ax_iter;
for (ax_iter = all_xceptions.begin(); ax_iter != all_xceptions.end(); ++ax_iter) {
generate_element(f_xsd_, ax_iter->first, ax_iter->second);
}
// Close the XSD document
f_xsd_ << '\n' << "</xsd:schema>" << '\n';
f_xsd_.close();
}
string t_xsd_generator::type_name(t_type* ttype) {
if (ttype->is_typedef()) {
return ttype->get_name();
}
if (ttype->is_base_type()) {
return xsd(base_type_name(((t_base_type*)ttype)->get_base()));
}
if (ttype->is_enum()) {
return xsd("int");
}
if (ttype->is_struct() || ttype->is_xception()) {
return ttype->get_name();
}
return "container";
}
/**
* Returns the XSD type that corresponds to the thrift type.
*
* @param tbase The base type
* @return Explicit XSD type, i.e. xsd:string
*/
string t_xsd_generator::base_type_name(t_base_type::t_base tbase) {
switch (tbase) {
case t_base_type::TYPE_VOID:
return "void";
case t_base_type::TYPE_STRING:
return "string";
case t_base_type::TYPE_BOOL:
return "boolean";
case t_base_type::TYPE_I8:
return "byte";
case t_base_type::TYPE_I16:
return "short";
case t_base_type::TYPE_I32:
return "int";
case t_base_type::TYPE_I64:
return "long";
case t_base_type::TYPE_DOUBLE:
return "decimal";
default:
throw "compiler error: no XSD base type name for base type " + t_base_type::t_base_name(tbase);
}
}
std::string t_xsd_generator::display_name() const {
return "XSD";
}
THRIFT_REGISTER_GENERATOR(xsd, "XSD", "")