1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sstream>
27 #include "thrift/version.h"
28 #include "thrift/platform.h"
29 #include "thrift/generate/t_generator.h"
30 
31 using std::map;
32 using std::ofstream;
33 using std::ostream;
34 using std::ostringstream;
35 using std::string;
36 using std::stringstream;
37 using std::vector;
38 
39 static const string endl = "\n"; // avoid ostream << std::endl flushes
40 
41 /**
42  * XSD generator, creates an XSD for the base types etc.
43  *
44  */
45 class t_xsd_generator : public t_generator {
46 public:
t_xsd_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)47   t_xsd_generator(t_program* program,
48                   const std::map<std::string, std::string>& parsed_options,
49                   const std::string& option_string)
50     : t_generator(program) {
51     (void)option_string;
52     std::map<std::string, std::string>::const_iterator iter;
53 
54     /* no options yet */
55     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
56       throw "unknown option xsd:" + iter->first;
57     }
58 
59     out_dir_base_ = "gen-xsd";
60   }
61 
62   ~t_xsd_generator() override = default;
63 
64   /**
65    * Init and close methods
66    */
67 
68   void init_generator() override;
69   void close_generator() override;
70 
71   /**
72    * Program-level generation functions
73    */
74 
75   void generate_typedef(t_typedef* ttypedef) override;
generate_enum(t_enum * tenum)76   void generate_enum(t_enum* tenum) override { (void)tenum; }
77 
78   void generate_service(t_service* tservice) override;
79   void generate_struct(t_struct* tstruct) override;
80 
81 private:
82   void generate_element(std::ostream& out,
83                         std::string name,
84                         t_type* ttype,
85                         t_struct* attrs = NULL,
86                         bool optional = false,
87                         bool nillable = false,
88                         bool list_element = false);
89 
ns(std::string in,std::string ns)90   std::string ns(std::string in, std::string ns) { return ns + ":" + in; }
91 
xsd(std::string in)92   std::string xsd(std::string in) { return ns(in, "xsd"); }
93 
94   std::string type_name(t_type* ttype);
95   std::string base_type_name(t_base_type::t_base tbase);
96 
xml_autogen_comment()97   virtual std::string xml_autogen_comment() {
98     return std::string("<!--\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
99            + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
100            + " -->\n";
101   }
102 
103   /**
104    * Output xsd/php file
105    */
106   ofstream_with_content_based_conditional_update f_xsd_;
107   ofstream_with_content_based_conditional_update f_php_;
108 
109   /**
110    * Output string stream
111    */
112   std::ostringstream s_xsd_types_;
113 };
114 
init_generator()115 void t_xsd_generator::init_generator() {
116   // Make output directory
117   MKDIR(get_out_dir().c_str());
118 
119   // Make output file
120   string f_php_name = get_out_dir() + program_->get_name() + "_xsd.php";
121   f_php_.open(f_php_name.c_str());
122 
123   f_php_ << "<?php" << endl
124          << autogen_comment() << endl;
125 }
126 
close_generator()127 void t_xsd_generator::close_generator() {
128   f_php_ << "?>" << endl;
129   f_php_.close();
130 }
131 
generate_typedef(t_typedef * ttypedef)132 void t_xsd_generator::generate_typedef(t_typedef* ttypedef) {
133   indent(s_xsd_types_) << "<xsd:simpleType name=\"" << ttypedef->get_name() << "\">" << endl;
134   indent_up();
135   if (ttypedef->get_type()->is_string() && ((t_base_type*)ttypedef->get_type())->is_string_enum()) {
136     indent(s_xsd_types_) << "<xsd:restriction base=\"" << type_name(ttypedef->get_type()) << "\">"
137                          << endl;
138     indent_up();
139     const vector<string>& values = ((t_base_type*)ttypedef->get_type())->get_string_enum_vals();
140     vector<string>::const_iterator v_iter;
141     for (v_iter = values.begin(); v_iter != values.end(); ++v_iter) {
142       indent(s_xsd_types_) << "<xsd:enumeration value=\"" << (*v_iter) << "\" />" << endl;
143     }
144     indent_down();
145     indent(s_xsd_types_) << "</xsd:restriction>" << endl;
146   } else {
147     indent(s_xsd_types_) << "<xsd:restriction base=\"" << type_name(ttypedef->get_type()) << "\" />"
148                          << endl;
149   }
150   indent_down();
151   indent(s_xsd_types_) << "</xsd:simpleType>" << endl << endl;
152 }
153 
generate_struct(t_struct * tstruct)154 void t_xsd_generator::generate_struct(t_struct* tstruct) {
155   vector<t_field*>::const_iterator m_iter;
156   const vector<t_field*>& members = tstruct->get_members();
157   bool xsd_all = tstruct->get_xsd_all();
158 
159   indent(s_xsd_types_) << "<xsd:complexType name=\"" << tstruct->get_name() << "\">" << endl;
160   indent_up();
161   if (xsd_all) {
162     indent(s_xsd_types_) << "<xsd:all>" << endl;
163   } else {
164     indent(s_xsd_types_) << "<xsd:sequence>" << endl;
165   }
166   indent_up();
167 
168   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
169     generate_element(s_xsd_types_,
170                      (*m_iter)->get_name(),
171                      (*m_iter)->get_type(),
172                      (*m_iter)->get_xsd_attrs(),
173                      (*m_iter)->get_xsd_optional() || xsd_all,
174                      (*m_iter)->get_xsd_nillable());
175   }
176 
177   indent_down();
178   if (xsd_all) {
179     indent(s_xsd_types_) << "</xsd:all>" << endl;
180   } else {
181     indent(s_xsd_types_) << "</xsd:sequence>" << endl;
182   }
183   indent_down();
184   indent(s_xsd_types_) << "</xsd:complexType>" << endl << endl;
185 }
186 
generate_element(ostream & out,string name,t_type * ttype,t_struct * attrs,bool optional,bool nillable,bool list_element)187 void t_xsd_generator::generate_element(ostream& out,
188                                        string name,
189                                        t_type* ttype,
190                                        t_struct* attrs,
191                                        bool optional,
192                                        bool nillable,
193                                        bool list_element) {
194   string sminOccurs = (optional || list_element) ? " minOccurs=\"0\"" : "";
195   string smaxOccurs = list_element ? " maxOccurs=\"unbounded\"" : "";
196   string soptional = sminOccurs + smaxOccurs;
197   string snillable = nillable ? " nillable=\"true\"" : "";
198 
199   if (ttype->is_void() || ttype->is_list()) {
200     indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">" << endl;
201     indent_up();
202     if (attrs == NULL && ttype->is_void()) {
203       indent(out) << "<xsd:complexType />" << endl;
204     } else {
205       indent(out) << "<xsd:complexType>" << endl;
206       indent_up();
207       if (ttype->is_list()) {
208         indent(out) << "<xsd:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" << endl;
209         indent_up();
210         string subname;
211         t_type* subtype = ((t_list*)ttype)->get_elem_type();
212         if (subtype->is_base_type() || subtype->is_container()) {
213           subname = name + "_elt";
214         } else {
215           subname = type_name(subtype);
216         }
217         f_php_ << "$GLOBALS['" << program_->get_name() << "_xsd_elt_" << name << "'] = '" << subname
218                << "';" << endl;
219         generate_element(out, subname, subtype, NULL, false, false, true);
220         indent_down();
221         indent(out) << "</xsd:sequence>" << endl;
222         indent(out) << "<xsd:attribute name=\"list\" type=\"xsd:boolean\" />" << endl;
223       }
224       if (attrs != NULL) {
225         const vector<t_field*>& members = attrs->get_members();
226         vector<t_field*>::const_iterator a_iter;
227         for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
228           indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
229                       << type_name((*a_iter)->get_type()) << "\" />" << endl;
230         }
231       }
232       indent_down();
233       indent(out) << "</xsd:complexType>" << endl;
234     }
235     indent_down();
236     indent(out) << "</xsd:element>" << endl;
237   } else {
238     if (attrs == NULL) {
239       indent(out) << "<xsd:element name=\"" << name << "\""
240                   << " type=\"" << type_name(ttype) << "\"" << soptional << snillable << " />"
241                   << endl;
242     } else {
243       // Wow, all this work for a SIMPLE TYPE with attributes?!?!?!
244       indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">"
245                   << endl;
246       indent_up();
247       indent(out) << "<xsd:complexType>" << endl;
248       indent_up();
249       indent(out) << "<xsd:complexContent>" << endl;
250       indent_up();
251       indent(out) << "<xsd:extension base=\"" << type_name(ttype) << "\">" << endl;
252       indent_up();
253       const vector<t_field*>& members = attrs->get_members();
254       vector<t_field*>::const_iterator a_iter;
255       for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
256         indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
257                     << type_name((*a_iter)->get_type()) << "\" />" << endl;
258       }
259       indent_down();
260       indent(out) << "</xsd:extension>" << endl;
261       indent_down();
262       indent(out) << "</xsd:complexContent>" << endl;
263       indent_down();
264       indent(out) << "</xsd:complexType>" << endl;
265       indent_down();
266       indent(out) << "</xsd:element>" << endl;
267     }
268   }
269 }
270 
271 
generate_service(t_service * tservice)272 void t_xsd_generator::generate_service(t_service* tservice) {
273   // Make output file
274   string f_xsd_name = get_out_dir() + tservice->get_name() + ".xsd";
275   f_xsd_.open(f_xsd_name.c_str());
276 
277   string ns = program_->get_namespace("xsd");
278   const std::map<std::string, std::string> annot = program_->get_namespace_annotations("xsd");
279   const std::map<std::string, std::string>::const_iterator uri = annot.find("uri");
280   if (uri != annot.end()) {
281     ns = uri->second;
282   }
283   if (ns.size() > 0) {
284     ns = " targetNamespace=\"" + ns + "\" xmlns=\"" + ns + "\" "
285          + "elementFormDefault=\"qualified\"";
286   }
287 
288   // Print the XSD header
289   f_xsd_ << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl
290          << "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" << ns << ">" << endl
291          << xml_autogen_comment()
292          << endl;
293 
294   // Print out the type definitions
295   indent(f_xsd_) << s_xsd_types_.str();
296 
297   // Keep a list of all the possible exceptions that might get thrown
298   map<string, t_struct*> all_xceptions;
299 
300   // List the elements that you might actually get
301   vector<t_function*> functions = tservice->get_functions();
302   vector<t_function*>::iterator f_iter;
303   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
304     string elemname = (*f_iter)->get_name() + "_response";
305     t_type* returntype = (*f_iter)->get_returntype();
306     generate_element(f_xsd_, elemname, returntype);
307     f_xsd_ << endl;
308 
309     t_struct* xs = (*f_iter)->get_xceptions();
310     const std::vector<t_field*>& xceptions = xs->get_members();
311     vector<t_field*>::const_iterator x_iter;
312     for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
313       all_xceptions[(*x_iter)->get_name()] = (t_struct*)((*x_iter)->get_type());
314     }
315   }
316 
317   map<string, t_struct*>::iterator ax_iter;
318   for (ax_iter = all_xceptions.begin(); ax_iter != all_xceptions.end(); ++ax_iter) {
319     generate_element(f_xsd_, ax_iter->first, ax_iter->second);
320   }
321 
322   // Close the XSD document
323   f_xsd_ << endl << "</xsd:schema>" << endl;
324   f_xsd_.close();
325 }
326 
type_name(t_type * ttype)327 string t_xsd_generator::type_name(t_type* ttype) {
328   if (ttype->is_typedef()) {
329     return ttype->get_name();
330   }
331 
332   if (ttype->is_base_type()) {
333     return xsd(base_type_name(((t_base_type*)ttype)->get_base()));
334   }
335 
336   if (ttype->is_enum()) {
337     return xsd("int");
338   }
339 
340   if (ttype->is_struct() || ttype->is_xception()) {
341     return ttype->get_name();
342   }
343 
344   return "container";
345 }
346 
347 /**
348  * Returns the XSD type that corresponds to the thrift type.
349  *
350  * @param tbase The base type
351  * @return Explicit XSD type, i.e. xsd:string
352  */
base_type_name(t_base_type::t_base tbase)353 string t_xsd_generator::base_type_name(t_base_type::t_base tbase) {
354   switch (tbase) {
355   case t_base_type::TYPE_VOID:
356     return "void";
357   case t_base_type::TYPE_STRING:
358     return "string";
359   case t_base_type::TYPE_BOOL:
360     return "boolean";
361   case t_base_type::TYPE_I8:
362     return "byte";
363   case t_base_type::TYPE_I16:
364     return "short";
365   case t_base_type::TYPE_I32:
366     return "int";
367   case t_base_type::TYPE_I64:
368     return "long";
369   case t_base_type::TYPE_DOUBLE:
370     return "decimal";
371   default:
372     throw "compiler error: no XSD base type name for base type " + t_base_type::t_base_name(tbase);
373   }
374 }
375 
376 THRIFT_REGISTER_GENERATOR(xsd, "XSD", "")
377