You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by je...@apache.org on 2016/01/10 11:54:27 UTC

thrift git commit: THRIFT-3523 XML Generator Client: XML Patch: Benjamin Gould, with minor modifications by Jens Geyer

Repository: thrift
Updated Branches:
  refs/heads/master f6521c64f -> de0b4b554


THRIFT-3523 XML Generator
Client: XML
Patch: Benjamin Gould, with minor modifications by Jens Geyer

This closes #774


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/de0b4b55
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/de0b4b55
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/de0b4b55

Branch: refs/heads/master
Commit: de0b4b55433fe47925a99cf17e6b99e584a3733c
Parents: f6521c6
Author: BCG <bg...@users.noreply.github.com>
Authored: Mon Dec 28 01:15:00 2015 -0500
Committer: Jens Geyer <je...@apache.org>
Committed: Sun Jan 10 11:54:04 2016 +0100

----------------------------------------------------------------------
 compiler/cpp/Makefile.am                     |   1 +
 compiler/cpp/compiler.vcxproj                |   1 +
 compiler/cpp/src/generate/t_xml_generator.cc | 683 ++++++++++++++++++++++
 configure.ac                                 |   2 +
 lib/Makefile.am                              |   2 +-
 lib/xml/Makefile.am                          |  29 +
 lib/xml/test/Makefile.am                     |  26 +
 lib/xml/test/build.xml                       | 112 ++++
 lib/xml/thrift-idl.xsd                       | 283 +++++++++
 9 files changed, 1138 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/compiler/cpp/Makefile.am
----------------------------------------------------------------------
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index 427062c..d8db10b 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -88,6 +88,7 @@ thrift_SOURCES += src/generate/t_c_glib_generator.cc \
                   src/generate/t_ocaml_generator.cc \
                   src/generate/t_hs_generator.cc \
                   src/generate/t_xsd_generator.cc \
+                  src/generate/t_xml_generator.cc \
                   src/generate/t_html_generator.cc \
                   src/generate/t_js_generator.cc \
                   src/generate/t_javame_generator.cc \

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/compiler/cpp/compiler.vcxproj
----------------------------------------------------------------------
diff --git a/compiler/cpp/compiler.vcxproj b/compiler/cpp/compiler.vcxproj
index fbd41ad..2987aad 100644
--- a/compiler/cpp/compiler.vcxproj
+++ b/compiler/cpp/compiler.vcxproj
@@ -79,6 +79,7 @@
     <ClCompile Include="src\generate\t_rb_generator.cc" />
     <ClCompile Include="src\generate\t_st_generator.cc" />
 	<ClCompile Include="src\generate\t_swift_generator.cc" />
+    <ClCompile Include="src\generate\t_xml_generator.cc" />
     <ClCompile Include="src\generate\t_xsd_generator.cc" />
     <ClCompile Include="src\main.cc" />
     <ClCompile Include="src\md5.c" />

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/compiler/cpp/src/generate/t_xml_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/generate/t_xml_generator.cc b/compiler/cpp/src/generate/t_xml_generator.cc
new file mode 100644
index 0000000..0b0eb05
--- /dev/null
+++ b/compiler/cpp/src/generate/t_xml_generator.cc
@@ -0,0 +1,683 @@
+/*
+ * 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 <limits>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "t_generator.h"
+#include "platform.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;
+using std::set;
+
+static const string endl = "\n";
+static const string quot = "\"";
+
+static const string default_ns_prefix = "http://thrift.apache.org/xml/ns/";
+
+/**
+ * This generator creates an XML model of the parsed IDL tree, and is designed
+ * to make it easy to use this file as the input for other template engines,
+ * such as XSLT.  To this end, the generated XML is slightly more verbose than
+ * you might expect... for example, references to "id" types (such as structs,
+ * unions, etc) always specify the name of the IDL document, even if the type
+ * is defined in the same document as the reference.
+ */
+class t_xml_generator : public t_generator {
+public:
+  t_xml_generator( t_program* program,
+                   const std::map<std::string, std::string>& parsed_options,
+                   const std::string& option_string)
+    : t_generator(program) {
+    (void)parsed_options;
+    (void)option_string;
+    out_dir_base_ = "gen-xml";
+
+    std::map<std::string, std::string>::const_iterator iter;
+    iter = parsed_options.find("merge");
+    should_merge_includes_ = (iter != parsed_options.end());
+
+    iter = parsed_options.find("no_default_ns");
+    should_use_default_ns_ = (iter == parsed_options.end());
+
+    iter = parsed_options.find("no_namespaces");
+    should_use_namespaces_ = (iter == parsed_options.end());
+  }
+
+  virtual ~t_xml_generator() {}
+
+  void init_generator();
+  void close_generator();
+  void generate_program();
+
+  void iterate_program(t_program* program);
+  void generate_typedef(t_typedef* ttypedef);
+  void generate_enum(t_enum* tenum);
+  void generate_function(t_function* tfunc);
+  void generate_field(t_field* field);
+
+  void generate_service(t_service* tservice);
+  void generate_struct(t_struct* tstruct);
+
+  void generate_annotations(std::map<std::string, std::string> annotations);
+
+private:
+  bool should_merge_includes_;
+  bool should_use_default_ns_;
+  bool should_use_namespaces_;
+
+  std::ofstream f_xml_;
+ 
+  std::set<string> programs_;
+  std::stack<string> elements_;
+  bool top_element_is_empty;
+  bool top_element_is_open;
+
+  string target_namespace(t_program* program);
+  void write_element_start(const string name);
+  void close_top_element();
+  void write_element_end();
+  void write_attribute(string key, string val);
+  void write_int_attribute(string key, int val);
+  string escape_xml_string(const string& input);
+
+  void write_xml_comment(string msg);
+
+  void write_type(t_type* ttype);
+  void write_doc(t_doc* tdoc);
+
+  template <typename T>
+  string number_to_string(T t) {
+    std::ostringstream out;
+    out.imbue(std::locale::classic());
+    out.precision(std::numeric_limits<T>::digits10);
+    out << t;
+    return out.str();
+  }
+
+  template <typename T>
+  void write_number(T n) {
+    f_xml_ << number_to_string(n);
+  }
+
+  template <typename T>
+  void write_element_number(string name, T n) {
+    write_element_string(name, number_to_string(n));
+  }
+
+  string get_type_name(t_type* ttype);
+
+  void generate_constant(t_const* con);
+
+  void write_element_string(string name, string value);
+  void write_value(t_type* tvalue);
+  void write_const_value(t_const_value* value);
+  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";
+  }
+};
+
+void t_xml_generator::init_generator() {
+  MKDIR(get_out_dir().c_str());
+
+  string f_xml_name = get_out_dir() + program_->get_name() + ".xml";
+  f_xml_.open(f_xml_name.c_str());
+
+  top_element_is_open = false;
+}
+
+string t_xml_generator::target_namespace(t_program* program) {
+  std::map<std::string, std::string> map;
+  std::map<std::string, std::string>::iterator iter;
+  map = program->get_namespace_annotations("xml");
+  if ((iter = map.find("targetNamespace")) != map.end()) {
+    return iter->second;
+  }
+  map = program->get_namespaces();
+  if ((iter = map.find("xml")) != map.end()) {
+    return default_ns_prefix + iter->second;
+  }
+  map = program->get_namespace_annotations("*");
+  if ((iter = map.find("xml.targetNamespace")) != map.end()) {
+    return iter->second;
+  }
+  map = program->get_namespaces();
+  if ((iter = map.find("*")) != map.end()) {
+    return default_ns_prefix + iter->second;
+  }
+  return default_ns_prefix + program->get_name();
+}
+
+void t_xml_generator::write_xml_comment(string msg) {
+  close_top_element();
+  // TODO: indent any EOLs that may occur with msg
+  // TODO: proper msg escaping needed?
+  f_xml_ << indent() << "<!-- " << msg << " -->"  << endl;  
+  top_element_is_empty = false;  
+}
+
+void t_xml_generator::close_top_element() {
+  if( top_element_is_open) {
+    top_element_is_open = false;
+    if (elements_.size() > 0 && top_element_is_empty) {
+      f_xml_ << ">" << endl;
+    }
+  }
+}
+
+void t_xml_generator::write_element_start(string name) {
+  if (should_use_namespaces_ && !should_use_default_ns_) {
+    name = "idl:" + name;
+  }
+  close_top_element();
+  f_xml_ << indent() << "<" << name;
+  elements_.push(name);
+  top_element_is_empty = true;
+  top_element_is_open = true;
+  indent_up();
+}
+
+void t_xml_generator::write_element_end() {
+  indent_down();
+  if (top_element_is_empty && top_element_is_open) {
+    f_xml_ << " />" << endl;
+  } else {
+    f_xml_ << indent() << "</" << elements_.top() << ">" << endl;
+  }
+  top_element_is_empty = false;
+  elements_.pop();
+}
+
+void t_xml_generator::write_attribute(string key, string val) {
+  f_xml_ << " " << key << "=\"" << escape_xml_string(val) << "\"";
+}
+
+void t_xml_generator::write_int_attribute(string key, int val) {
+  write_attribute(key, number_to_string(val));
+}
+
+void t_xml_generator::write_element_string(string name, string val) {
+  if (should_use_namespaces_ && !should_use_default_ns_) {
+    name = "idl:" + name;
+  }
+  close_top_element();
+  top_element_is_empty = false;
+  f_xml_ << indent() 
+    << "<" << name << ">" << escape_xml_string(val) << "</" << name << ">"
+    << endl;
+}
+
+string t_xml_generator::escape_xml_string(const string& input) {
+  std::ostringstream ss;
+  for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) {
+    switch (*iter) {
+    case '&':
+      ss << "&amp;";
+      break;
+    case '"':
+      ss << "&quot;";
+      break;
+    case '\'':
+      ss << "&apos;";
+      break;
+    case '<':
+      ss << "&lt;";
+      break;
+    case '>':
+      ss << "&gt;";
+      break;
+    default:
+      ss << *iter;
+      break;
+    }
+  }
+  return ss.str();
+}
+
+void t_xml_generator::close_generator() {
+  f_xml_.close();
+}
+
+void t_xml_generator::generate_program() {
+
+  init_generator();
+
+  write_element_start("idl");
+  if (should_use_namespaces_) {
+    if (should_use_default_ns_) {
+      write_attribute("xmlns", "http://thrift.apache.org/xml/idl");
+    }
+    write_attribute("xmlns:idl", "http://thrift.apache.org/xml/idl");
+  }
+
+  write_xml_comment( xml_autogen_comment());
+  
+  iterate_program(program_);
+  
+  write_element_end();
+
+  close_generator();
+
+}
+
+void t_xml_generator::iterate_program(t_program* program) {
+  
+  write_element_start("document");
+  write_attribute("name", program->get_name());
+  if (should_use_namespaces_) {
+    const string targetNamespace = target_namespace(program);
+    write_attribute("targetNamespace", targetNamespace);
+    write_attribute("xmlns:" + program->get_name(), targetNamespace);
+  }
+  write_doc(program);
+
+  const vector<t_program*> includes = program->get_includes();
+  vector<t_program*>::const_iterator inc_it;
+  for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) {
+    const string include_path = (*inc_it)->get_path();
+    write_element_start("include");
+    write_attribute("name", (*inc_it)->get_name());
+    write_attribute("file", include_path);
+    write_element_end();
+  }
+
+  const map<string, string>& namespaces = program->get_namespaces();
+  map<string, string>::const_iterator ns_it;
+  for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) {
+    write_element_start("namespace");
+    write_attribute("name", ns_it->first);
+    write_attribute("value", ns_it->second);
+    generate_annotations(program->get_namespace_annotations(ns_it->first));
+    write_element_end();
+  }
+
+  // TODO: can constants have annotations?
+  vector<t_const*> consts = program->get_consts();
+  vector<t_const*>::iterator c_iter;
+  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+    generate_constant(*c_iter);
+  }
+
+  vector<t_typedef*> typedefs = program->get_typedefs();
+  vector<t_typedef*>::iterator td_iter;
+  for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+    generate_typedef(*td_iter);
+  }
+
+  vector<t_enum*> enums = program->get_enums();
+  vector<t_enum*>::iterator en_iter;
+  for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+    generate_enum(*en_iter);
+  }
+
+  vector<t_struct*> objects = program->get_objects();
+  vector<t_struct*>::iterator o_iter;
+  for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+    if ((*o_iter)->is_xception()) {
+      generate_xception(*o_iter);
+    } else {
+      generate_struct(*o_iter);
+    }
+  }
+
+  vector<t_service*> services = program->get_services();
+  vector<t_service*>::iterator sv_iter;
+  for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+    generate_service(*sv_iter);
+  }
+
+  write_element_end();
+
+  if (should_merge_includes_) {
+    programs_.insert(program->get_name());
+    const vector<t_program*> programs = program->get_includes();
+    vector<t_program*>::const_iterator prog_it;
+    for (prog_it = programs.begin(); prog_it != programs.end(); ++prog_it) {
+      if (!programs_.count((*prog_it)->get_name())) {
+        iterate_program(*prog_it);
+      }
+    }
+  }
+
+}
+
+void t_xml_generator::generate_typedef(t_typedef* ttypedef) {
+  write_element_start("typedef");
+  write_attribute("name", ttypedef->get_name());
+  write_doc(ttypedef);
+  write_type(ttypedef->get_true_type());
+  generate_annotations(ttypedef->annotations_);
+  write_element_end();
+  return;
+}
+
+void t_xml_generator::write_type(t_type* ttype) {
+  const string type = get_type_name(ttype);
+  write_attribute("type", type);
+  if (type == "id") {
+    write_attribute("type-module", ttype->get_program()->get_name());
+    write_attribute("type-id", ttype->get_name());
+  } else if (type == "list" || type == "set") {
+    t_type* etype = ((t_list*)ttype)->get_elem_type();
+    write_element_start("elemType");
+    write_type(etype);
+    write_element_end();
+  } else if (type == "map") {
+    t_type* ktype = ((t_map*)ttype)->get_key_type();
+    write_element_start("keyType");
+    write_type(ktype);
+    write_element_end();
+    t_type* vtype = ((t_map*)ttype)->get_val_type();
+    write_element_start("valueType");
+    write_type(vtype);
+    write_element_end();
+  }
+}
+
+void t_xml_generator::write_doc(t_doc* tdoc) {
+  if (tdoc->has_doc()) {
+    string doc = tdoc->get_doc();
+    // for some reason there always seems to be a trailing newline on doc
+    // comments; loop below naively tries to strip off trailing cr/lf
+    int n = 0;
+    for (string::reverse_iterator i = doc.rbegin(); i != doc.rend(); i++,n++) {
+      if (*i != '\n' || *i == '\r') {
+        if (n > 0) {
+          doc.erase(doc.length() - n);
+        }
+        break;
+      }
+    }
+    write_attribute("doc", doc);
+  }
+}
+
+void t_xml_generator::generate_annotations(
+    std::map<std::string, std::string> annotations) {
+  std::map<std::string, std::string>::iterator iter;
+  for (iter = annotations.begin(); iter != annotations.end(); ++iter) {
+    write_element_start("annotation");
+    write_attribute("key", iter->first);
+    write_attribute("value", iter->second);
+    write_element_end();
+  }
+}
+
+void t_xml_generator::generate_constant(t_const* con) {
+  write_element_start("const");
+  write_attribute("name", con->get_name());
+  write_doc(con);
+  write_type(con->get_type());
+  write_const_value(con->get_value());
+  write_element_end();
+}
+
+void t_xml_generator::write_const_value(t_const_value* value) {
+
+  switch (value->get_type()) {
+
+  case t_const_value::CV_IDENTIFIER:
+  case t_const_value::CV_INTEGER:
+    write_element_number("int", value->get_integer());
+    break;
+
+  case t_const_value::CV_DOUBLE:
+    write_element_number("double", value->get_double());
+    break;
+
+  case t_const_value::CV_STRING:
+    write_element_string("string", value->get_string());
+    break;
+
+  case t_const_value::CV_LIST: {
+    write_element_start("list");
+    std::vector<t_const_value*> list = value->get_list();
+    std::vector<t_const_value*>::iterator lit;
+    for (lit = list.begin(); lit != list.end(); ++lit) {
+      write_element_start("entry");
+      write_const_value(*lit);
+      write_element_end();
+    }
+    write_element_end();
+    break;
+  }
+
+  case t_const_value::CV_MAP: {
+    write_element_start("map");
+    std::map<t_const_value*, t_const_value*> map = value->get_map();
+    std::map<t_const_value*, t_const_value*>::iterator mit;
+    for (mit = map.begin(); mit != map.end(); ++mit) {
+      write_element_start("entry");
+      write_element_start("key");
+      write_const_value(mit->first);
+      write_element_end();
+      write_element_start("value");
+      write_const_value(mit->second);
+      write_element_end();
+      write_element_end();
+    }
+    write_element_end();
+    break;
+  }
+
+  default:
+    indent_up();
+    f_xml_ << indent() << "<null />" << endl;
+    indent_down();
+    break;
+  }
+
+}
+
+void t_xml_generator::generate_enum(t_enum* tenum) {
+
+  write_element_start("enum");
+  write_attribute("name", tenum->get_name());
+  write_doc(tenum);
+
+  vector<t_enum_value*> values = tenum->get_constants();
+  vector<t_enum_value*>::iterator val_iter;
+  for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+    t_enum_value* val = (*val_iter);
+    write_element_start("member");
+    write_attribute("name", val->get_name());
+    write_int_attribute("value", val->get_value());
+    write_doc(val);
+    generate_annotations(val->annotations_);
+    write_element_end();
+  }
+
+  generate_annotations(tenum->annotations_);
+
+  write_element_end();
+
+}
+
+void t_xml_generator::generate_struct(t_struct* tstruct) {
+
+  string tagname = "struct";
+  if (tstruct->is_union()) {
+    tagname = "union";
+  } else if (tstruct->is_xception()) {
+    tagname = "exception";
+  }
+
+  write_element_start(tagname);
+  write_attribute("name", tstruct->get_name());
+  write_doc(tstruct);
+  vector<t_field*> members = tstruct->get_members();
+  vector<t_field*>::iterator mem_iter;
+  for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) {
+    write_element_start("field");
+    generate_field(*mem_iter);
+    write_element_end();
+  }
+
+  generate_annotations(tstruct->annotations_);
+ 
+  write_element_end();
+
+}
+
+void t_xml_generator::generate_field(t_field* field) {
+  write_attribute("name", field->get_name());
+  write_int_attribute("field-id", field->get_key());
+  write_doc(field); 
+  string requiredness;
+  switch (field->get_req()) {
+  case t_field::T_REQUIRED:
+    requiredness = "required";
+    break;
+  case t_field::T_OPTIONAL:
+    requiredness = "optional";
+    break;
+  default:
+    requiredness = "";
+    break;
+  }
+  if (requiredness != "") {
+    write_attribute("required", requiredness);
+  }
+  write_type(field->get_type());
+  if (field->get_value()) {
+    write_element_start("default");
+    write_const_value(field->get_value());
+    write_element_end();
+  }
+  generate_annotations(field->annotations_);
+}
+
+void t_xml_generator::generate_service(t_service* tservice) {
+ 
+  write_element_start("service");
+  write_attribute("name", tservice->get_name());
+
+  if (should_use_namespaces_) {
+    string prog_ns = target_namespace(tservice->get_program());
+    if (*prog_ns.rbegin() != '/') {
+      prog_ns.push_back('/');
+    }
+    const string tns = prog_ns + tservice->get_name();
+    write_attribute("targetNamespace", tns);
+    write_attribute("xmlns:tns", tns);
+  }
+ 
+  if (tservice->get_extends()) {
+    const t_service* extends = tservice->get_extends();
+    write_attribute("parent-module", extends->get_program()->get_name());
+    write_attribute("parent-id", extends->get_name());
+  }
+
+  write_doc(tservice);
+
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::iterator fn_iter = functions.begin();
+  for (; fn_iter != functions.end(); fn_iter++) {
+    generate_function(*fn_iter);
+  }
+  
+  generate_annotations(tservice->annotations_);
+  
+  write_element_end();
+
+}
+
+void t_xml_generator::generate_function(t_function* tfunc) {
+ 
+  write_element_start("method");
+
+  write_attribute("name", tfunc->get_name());
+  if (tfunc->is_oneway()) {
+    write_attribute("oneway", "true");
+  }
+
+  write_doc(tfunc);
+
+  write_element_start("returns");
+  write_type(tfunc->get_returntype());
+  write_element_end();
+
+  vector<t_field*> members = tfunc->get_arglist()->get_members();
+  vector<t_field*>::iterator mem_iter = members.begin();
+  for (; mem_iter != members.end(); mem_iter++) {
+    write_element_start("arg");
+    generate_field(*mem_iter);
+    write_element_end();
+  }
+ 
+  vector<t_field*> excepts = tfunc->get_xceptions()->get_members();
+  vector<t_field*>::iterator ex_iter = excepts.begin();
+  for (; ex_iter != excepts.end(); ex_iter++) {
+    write_element_start("throws");
+    generate_field(*ex_iter);
+    write_element_end();
+  }
+
+  generate_annotations(tfunc->annotations_);
+  
+  write_element_end();
+
+}
+
+string t_xml_generator::get_type_name(t_type* ttype) {
+  if (ttype->is_list()) {
+    return "list";
+  }
+  if (ttype->is_set()) {
+    return "set";
+  }
+  if (ttype->is_map()) {
+    return "map";
+  }
+  if ((ttype->is_enum()    )|| 
+      (ttype->is_struct()  )|| 
+      (ttype->is_typedef() )|| 
+      (ttype->is_xception())){
+    return "id";
+  }
+  if (ttype->is_base_type()) {
+    t_base_type* tbasetype = (t_base_type*)ttype;
+    if (tbasetype->is_binary() ) {
+      return "binary";
+    }
+    return t_base_type::t_base_name(tbasetype->get_base());
+  }
+  return "(unknown)";
+}
+
+THRIFT_REGISTER_GENERATOR(
+  xml,
+  "XML",
+  "    merge:           Generate output with included files merged\n"
+  "    no_default_ns:   Omit default xmlns and add idl: prefix to all elements\n"
+  "    no_namespaces:   Do not add namespace definitions to the XML model\n")

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 141b542..3a1314d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -753,6 +753,8 @@ AC_CONFIG_FILES([
   lib/py/Makefile
   lib/rb/Makefile
   lib/lua/Makefile
+  lib/xml/Makefile
+  lib/xml/test/Makefile
   test/Makefile
   test/c_glib/Makefile
   test/cpp/Makefile

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/lib/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 650f382..e699b9f 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-SUBDIRS = json
+SUBDIRS = json xml 
 PRECROSS_TARGET =
 
 if WITH_CPP

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/lib/xml/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/xml/Makefile.am b/lib/xml/Makefile.am
new file mode 100644
index 0000000..bcad6bd
--- /dev/null
+++ b/lib/xml/Makefile.am
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+SUBDIRS =
+
+if WITH_JAVA
+# Schema validation test depends on java
+SUBDIRS += test
+endif
+
+EXTRA_DIST = \
+  thrift-idl.xsd \
+  test

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/lib/xml/test/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/xml/test/Makefile.am b/lib/xml/test/Makefile.am
new file mode 100644
index 0000000..bb87a52
--- /dev/null
+++ b/lib/xml/test/Makefile.am
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+check:
+	$(ANT) $(ANT_FLAGS) test
+
+# Make sure this doesn't fail if ant is not configured.
+clean-local:
+	ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
+	$$ANT $(ANT_FLAGS) clean

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/lib/xml/test/build.xml
----------------------------------------------------------------------
diff --git a/lib/xml/test/build.xml b/lib/xml/test/build.xml
new file mode 100644
index 0000000..f0e95cf
--- /dev/null
+++ b/lib/xml/test/build.xml
@@ -0,0 +1,112 @@
+<!--
+ 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.
+-->
+<project name="XML Schema Test" default="test" basedir=".">
+
+  <description>XML Schema Validation Test</description>
+
+  <property name="xml.dir" location="${basedir}/.." />
+  <property name="gen.xml.dir" location="${basedir}/../gen-xml" />
+  <property name="idl.xml.schema" location="${xml.dir}/thrift-idl.xsd" />
+
+  <property name="thrift.dir" location="../../../" />
+  <property name="thrift.test.dir" location="${thrift.dir}/test" />
+  <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+  <property file="${basedir}/build.properties" />
+
+  <target name="compiler.check">
+    <fail>
+      <condition>
+        <not>
+          <resourcecount count="1">
+            <fileset id="fs" file="${thrift.compiler}"/>
+          </resourcecount>
+        </not>
+      </condition>
+      Thrift compiler is missing !
+    </fail>
+  </target>
+
+  <target name="init" depends="compiler.check, mkdirs">
+    <tstamp />
+  </target>
+
+  <target name="mkdirs">
+    <mkdir dir="${gen.xml.dir}"/>
+  </target>
+
+  <target name="generate" depends="init">
+    <generate-xml file="${thrift.test.dir}/ThriftTest.thrift"/>
+    <generate-xml file="${thrift.test.dir}/Include.thrift"/>
+    <generate-xml file="${thrift.test.dir}/Recursive.thrift"/>
+    <generate-xml file="${thrift.test.dir}/ManyOptionals.thrift"/>
+    <generate-xml file="${thrift.test.dir}/OptionalRequiredTest.thrift"/>
+    <generate-xml file="${thrift.test.dir}/ConstantsDemo.thrift"/>
+    <generate-xml file="${thrift.test.dir}/TypedefTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/AnnotationTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/DocTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/EnumTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/ManyTypedefs.thrift" />
+  </target>
+
+  <target name="test" description="run schema validation"
+          depends="validate-generated-xml"/>
+    
+  <target name="validate-generated-xml" depends="init, generate">
+    <validate-xml file="${gen.xml.dir}/ThriftTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/Include.xml"/>
+    <validate-xml file="${gen.xml.dir}/Recursive.xml"/>
+    <validate-xml file="${gen.xml.dir}/ManyOptionals.xml"/>
+    <validate-xml file="${gen.xml.dir}/OptionalRequiredTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/ConstantsDemo.xml"/>
+    <validate-xml file="${gen.xml.dir}/TypedefTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/AnnotationTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/DocTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/EnumTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/ManyTypedefs.xml"/>
+  </target>
+
+  <target name="clean">
+    <delete dir="${build.dir}" />
+    <delete dir="${gen.xml.dir}" />
+  </target>
+
+  <macrodef name="generate-xml">
+    <attribute name="file" />
+    <sequential>
+      <exec executable="${thrift.compiler}" failonerror="true">
+        <arg line="-gen xml:merge"/>
+        <arg line="-out ${gen.xml.dir}"/>
+        <arg line="@{file}"/>
+      </exec>
+    </sequential>
+  </macrodef>
+
+  <macrodef name="validate-xml">
+    <attribute name="file" />
+    <sequential>
+      <echo message="validating generated XML: @{file}" />
+      <schemavalidate file="@{file}">
+        <schema namespace="http://thrift.apache.org/xml/idl" 
+                file="${idl.xml.schema}" />
+      </schemavalidate>
+    </sequential>
+  </macrodef>
+
+</project>

http://git-wip-us.apache.org/repos/asf/thrift/blob/de0b4b55/lib/xml/thrift-idl.xsd
----------------------------------------------------------------------
diff --git a/lib/xml/thrift-idl.xsd b/lib/xml/thrift-idl.xsd
new file mode 100644
index 0000000..7a5248a
--- /dev/null
+++ b/lib/xml/thrift-idl.xsd
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://thrift.apache.org/xml/idl" 
+        xmlns:tns="http://thrift.apache.org/xml/idl" 
+        elementFormDefault="qualified">
+
+  <element name="idl" type="tns:IDL" />
+  <element name="document" type="tns:Document" />
+
+  <complexType name="IDL">
+    <sequence>
+      <element ref="tns:document" minOccurs="1" maxOccurs="unbounded"/>
+    </sequence>
+  </complexType>
+
+  <complexType name="Document">
+    <sequence>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="include" type="tns:Include" />
+        <element name="namespace" type="tns:Namespace" />
+      </choice>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="exception" type="tns:Exception" />
+        <element name="typedef" type="tns:Typedef" />
+        <element name="service" type="tns:Service" />
+        <element name="struct" type="tns:Struct" />
+        <element name="const" type="tns:Const" />
+        <element name="union" type="tns:Union" />
+        <element name="enum" type="tns:Enum" />
+      </choice>
+    </sequence>
+    <attribute name="name" type="string" />
+    <attribute name="targetNamespace" type="anyURI" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Include">
+    <attribute name="file" type="string" />
+    <attribute name="name" type="string" />
+  </complexType>
+
+  <complexType name="Namespace">
+    <sequence>
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" />
+    <attribute name="value" type="string" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <group name="AbstractStruct">
+    <sequence>
+      <element name="field" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+  </group>
+
+  <attributeGroup name="StructAttributes">
+    <attribute name="name" type="string" />
+    <attribute name="doc" type="string" />
+  </attributeGroup>
+
+  <complexType name="Exception">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Service">
+    <sequence>
+      <element name="method" type="tns:Method" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="targetNamespace" type="string" use="required" />
+    <attribute name="parent-module" type="string" use="optional" />
+    <attribute name="parent-id" type="string" use="optional" /> 
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Method">
+    <sequence>
+      <element name="returns" type="tns:ThriftType" />
+      <element name="arg" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="throws" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="oneway" type="boolean" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Typedef">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <element name="annotation" type="tns:Annotation" 
+                   minOccurs="0" maxOccurs="unbounded" />
+        </sequence>
+        <attribute name="name" type="string" use="required" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <complexType name="Struct">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Union">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Enum">
+    <sequence>
+      <element name="member" minOccurs="1" maxOccurs="unbounded">
+        <complexType>
+          <sequence>
+            <element name="annotation" type="tns:Annotation" 
+                     minOccurs="0" maxOccurs="unbounded" />
+          </sequence>
+          <attribute name="name" type="string" use="required" />
+          <attribute name="value" type="int" />
+          <attribute name="explicit" type="boolean" />
+          <attribute name="doc" type="string" />
+        </complexType>
+      </element>
+      <element name="annotation" type="tns:Annotation" 
+                minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Field">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <element name="default" minOccurs="0" maxOccurs="1">
+            <complexType>
+              <group ref="tns:ConstValue" />
+            </complexType>
+          </element>
+          <element name="annotation" type="tns:Annotation" 
+                   minOccurs="0" maxOccurs="unbounded" />
+        </sequence>
+        <attribute name="field-id" type="long" />
+        <attribute name="name" type="string" use="required" />
+        <attribute name="required" type="tns:Requiredness" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <simpleType name="Requiredness">
+    <restriction base="string">
+      <enumeration value="required" />
+      <enumeration value="optional" />
+    </restriction>
+  </simpleType>
+
+  <complexType name="Annotation">
+    <attribute name="key" type="string" />
+    <attribute name="value" type="string" />
+  </complexType>
+
+  <complexType name="Const">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <group ref="tns:ConstValue" />
+        </sequence>
+        <attribute name="name" type="string" use="required" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <complexType name="ConstList">
+    <sequence>
+      <element name="entry" minOccurs="0" maxOccurs="unbounded">
+        <complexType>
+          <group ref="tns:ConstValue" />
+        </complexType>
+      </element>
+    </sequence>
+  </complexType>
+
+  <complexType name="ConstMap">
+    <sequence>
+      <element name="entry" minOccurs="0" maxOccurs="unbounded">
+        <complexType>
+          <sequence>
+            <element name="key">
+              <complexType>
+                <group ref="tns:ConstValue" />
+              </complexType>
+            </element>
+            <element name="value">
+              <complexType>
+                <group ref="tns:ConstValue" />
+              </complexType>
+            </element>
+          </sequence>
+        </complexType>
+      </element>
+    </sequence>
+  </complexType>
+
+  <group name="ConstValue">
+    <choice>
+      <element name="string" type="string" />
+      <element name="double" type="double" />
+      <element name="list" type="tns:ConstList" />
+      <element name="map" type="tns:ConstMap" />
+      <element name="int" type="long" />
+    </choice>
+  </group>
+
+  <complexType name="ThriftType">
+    <sequence>
+      <choice minOccurs="0" maxOccurs="1">
+        <element name="elemType" type="tns:ThriftType" />
+        <sequence>
+          <element name="keyType" type="tns:ThriftType" 
+                   minOccurs="1" maxOccurs="1" />
+          <element name="valueType" type="tns:ThriftType" 
+                   minOccurs="1" maxOccurs="1" />
+        </sequence>
+      </choice>
+    </sequence>
+    <attribute name="type" type="tns:TypeIdentifier" use="required" />
+    <attribute name="type-module" type="string" />
+    <attribute name="type-id" type="string" />
+  </complexType>
+
+  <simpleType name="TypeIdentifier">
+    <restriction base="string">
+      <enumeration value="void" />
+      <enumeration value="bool" />
+      <enumeration value="byte" />
+      <enumeration value="i8" />
+      <enumeration value="i16" />
+      <enumeration value="i32" />
+      <enumeration value="i64" />
+      <enumeration value="double" />
+      <enumeration value="binary" />
+      <enumeration value="string" />
+      <enumeration value="id" />
+      <enumeration value="map" />
+      <enumeration value="set" />
+      <enumeration value="list" />
+    </restriction>
+  </simpleType>
+
+</schema>