Thrift now generates XSD files

Summary: Are you fucking kidding me? This shit is robust.

Reviewed By: fetter-tbr-d00d


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664928 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/Makefile b/compiler/cpp/Makefile
index 3d405a1..3b9be48 100644
--- a/compiler/cpp/Makefile
+++ b/compiler/cpp/Makefile
@@ -35,6 +35,7 @@
 # Source files
 SRC_FILES = main.cc \
             generate/t_generator.cc \
+            generate/t_xsd_generator.cc \
             generate/t_py_generator.cc \
             generate/t_java_generator.cc \
             generate/t_php_generator.cc \
diff --git a/compiler/cpp/src/generate/t_generator.h b/compiler/cpp/src/generate/t_generator.h
index 50f7bf6..5e835a2 100644
--- a/compiler/cpp/src/generate/t_generator.h
+++ b/compiler/cpp/src/generate/t_generator.h
@@ -5,6 +5,7 @@
 #include <iostream>
 #include <sstream>
 #include "parse/t_program.h"
+#include "globals.h"
 
 /**
  * Base class for a thrift code generator. This class defines the basic
diff --git a/compiler/cpp/src/generate/t_xsd_generator.cc b/compiler/cpp/src/generate/t_xsd_generator.cc
new file mode 100644
index 0000000..cb184a3
--- /dev/null
+++ b/compiler/cpp/src/generate/t_xsd_generator.cc
@@ -0,0 +1,178 @@
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "t_xsd_generator.h"
+using namespace std;
+
+void t_xsd_generator::init_generator() {
+  // Make output directory
+  mkdir(T_XSD_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+}
+
+void t_xsd_generator::close_generator() {
+}
+
+void t_xsd_generator::generate_typedef(t_typedef* ttypedef) {
+  indent(s_xsd_types_) <<
+    "<xsd:simpleType name=\"" << ttypedef->get_name() << "\">" << endl;
+  indent_up();
+  indent(s_xsd_types_) <<
+    "<xs:restriction base=\"" << type_name(ttypedef->get_type()) << "\" />" << endl;
+  indent_down();
+  indent(s_xsd_types_) <<
+    "</xsd:simpleType>" << endl << endl;
+}
+
+void t_xsd_generator::generate_struct(t_struct* tstruct) {
+  vector<t_field*>::const_iterator m_iter; 
+  const vector<t_field*>& members = tstruct->get_members();
+  
+  indent(s_xsd_types_) << "<xsd:complexType name=\"" << tstruct->get_name() << "\">" << endl;
+  indent_up();
+  indent(s_xsd_types_) << "<xsd:sequence>" << endl;
+  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());
+  } 
+
+  indent_down();
+  indent(s_xsd_types_) << "</xsd:sequence>" << endl;
+  indent_down();
+  indent(s_xsd_types_) <<
+    "</xsd:complexType>" << endl <<
+    endl;
+}
+
+void t_xsd_generator::generate_element(ostream& out,
+                                       string name,
+                                       t_type* ttype) {
+  if (ttype->is_void() || ttype->is_list()) {
+    indent(out) <<
+      "<xsd:element name=\"" << name << "\">" << endl;
+    indent_up();
+    if (ttype->is_void()) {
+      indent(out) << 
+        "<xsd:complexType />" << endl;
+    } else if (ttype->is_list()) {
+      indent(out) << "<xsd:complexType>" << endl;
+      indent_up();
+      indent(out) << "<xsd:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" << endl;
+      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);
+      }
+      generate_element(out, subname, subtype);
+      indent_down();
+      indent(out) << "</xsd:sequence>" << endl;
+      indent(out) << "<xsd:attribute name=\"list\" type=\"xsd:boolean\" />" << endl;
+      indent_down();
+      indent(out) << "</xsd:complexType>" << endl;
+    }
+    indent_down();
+    indent(out) <<
+      "</xsd:element>" << endl;
+  } else {
+    indent(out) <<
+      "<xsd:element name=\"" << name << "\" type=\"" << type_name(ttype) << "\" />" << endl;
+  }
+}
+
+void t_xsd_generator::generate_service(t_service* tservice) {
+  // Make output file
+  string f_xsd_name = string(T_XSD_DIR)+"/"+tservice->get_name()+".xsd";
+  f_xsd_.open(f_xsd_name.c_str());
+
+  // Print the XSD header
+  f_xsd_ <<
+    "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl <<
+    "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" << endl <<
+    endl <<
+    "<!-- Yo yo yo, this XSD woz be generated by Thrift. -->" << endl <<
+    endl;
+
+  // Print out the type definitions
+  indent(f_xsd_) << s_xsd_types_.str();
+
+  // Keep a list of all the possible exceptions that might get thrown
+  set<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() + "_result";
+    t_type* returntype = (*f_iter)->get_returntype();
+    generate_element(f_xsd_, elemname, returntype);
+    f_xsd_ << endl;
+
+    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.insert((t_struct*)((*x_iter)->get_type()));
+    }
+  }
+
+  set<t_struct*>::iterator ax_iter;
+  for (ax_iter = all_xceptions.begin(); ax_iter != all_xceptions.end(); ++ax_iter) {
+    generate_element(f_xsd_, (*ax_iter)->get_name(), *ax_iter);
+  }
+
+  // Close the XSD document
+  f_xsd_ << endl << "</xsd:schema>" << endl;
+  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_BYTE:
+    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 C++ base type name for base type " + tbase;
+  }
+}
diff --git a/compiler/cpp/src/generate/t_xsd_generator.h b/compiler/cpp/src/generate/t_xsd_generator.h
new file mode 100644
index 0000000..fc39765
--- /dev/null
+++ b/compiler/cpp/src/generate/t_xsd_generator.h
@@ -0,0 +1,68 @@
+#ifndef T_XSD_GENERATOR_H
+#define T_XSD_GENERATOR_H
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include "t_generator.h"
+
+// TODO(mcslee): Paramaterize the output dir
+#define T_XSD_DIR "gen-xsd"
+
+/**
+ * XSD generator, creates an XSD for the base types etc.
+ *
+ * @author Mark Slee <mcslee@facebook.com>
+ */
+class t_xsd_generator : public t_generator {
+ public:
+  t_xsd_generator(t_program* program) :
+    t_generator(program) {}
+
+  virtual ~t_xsd_generator() {}
+
+  /**
+   * Init and close methods
+   */
+
+  void init_generator();
+  void close_generator();
+
+  /**
+   * Program-level generation functions
+   */
+
+  void generate_typedef(t_typedef* ttypedef);
+  void generate_enum(t_enum* tenum) {}
+
+  void generate_service(t_service* tservice);
+  void generate_struct(t_struct* tstruct);
+
+ private:
+
+  void generate_element(std::ostream& out, std::string name, t_type* ttype);
+
+  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);
+
+  /**
+   * Output xsd file
+   */
+  std::ofstream f_xsd_;
+
+  /**
+   * Output string stream
+   */
+  std::ostringstream s_xsd_types_;
+
+};
+
+#endif
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index 7280a37..065c95f 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -26,6 +26,7 @@
 #include "generate/t_java_generator.h"
 #include "generate/t_php_generator.h"
 #include "generate/t_py_generator.h"
+#include "generate/t_xsd_generator.h"
 
 using namespace std;
 
@@ -108,6 +109,7 @@
 bool gen_cpp = false;
 bool gen_java = false;
 bool gen_py = false;
+bool gen_xsd = false;
 bool gen_php = false;
 bool gen_phpi = false;
 bool gen_recurse = false;
@@ -515,6 +517,14 @@
       py->generate_program();
       delete py;
     }
+
+    if (gen_xsd) {
+      pverbose("Generating XSD\n");
+      t_xsd_generator* xsd = new t_xsd_generator(program);
+      xsd->generate_program();
+      delete xsd;
+    }
+
   } catch (string s) {
     printf("Error: %s\n", s.c_str());
   } catch (const char* exc) {
@@ -571,6 +581,8 @@
         gen_phpi = true;
       } else if (strcmp(arg, "-py") == 0) {
         gen_py = true;
+      } else if (strcmp(arg, "-xsd") == 0) {
+        gen_xsd = true;
       } else if (strcmp(arg, "-I") == 0) {
         // An argument of "-I\ asdf" is invalid and has unknown results
         arg = argv[++i];
@@ -591,7 +603,7 @@
   }
   
   // You gotta generate something!
-  if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py) {
+  if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py && !gen_xsd) {
     fprintf(stderr, "!!! No output language(s) specified\n\n");
     usage();
   }